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 "cc/layers/nine_patch_layer_impl.h" 6 7 #include "base/strings/stringprintf.h" 8 #include "base/values.h" 9 #include "cc/base/math_util.h" 10 #include "cc/layers/quad_sink.h" 11 #include "cc/quads/texture_draw_quad.h" 12 #include "cc/trees/layer_tree_impl.h" 13 #include "ui/gfx/rect_f.h" 14 15 namespace cc { 16 17 NinePatchLayerImpl::NinePatchLayerImpl(LayerTreeImpl* tree_impl, int id) 18 : UIResourceLayerImpl(tree_impl, id), 19 fill_center_(false) {} 20 21 NinePatchLayerImpl::~NinePatchLayerImpl() {} 22 23 scoped_ptr<LayerImpl> NinePatchLayerImpl::CreateLayerImpl( 24 LayerTreeImpl* tree_impl) { 25 return NinePatchLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>(); 26 } 27 28 void NinePatchLayerImpl::PushPropertiesTo(LayerImpl* layer) { 29 UIResourceLayerImpl::PushPropertiesTo(layer); 30 NinePatchLayerImpl* layer_impl = static_cast<NinePatchLayerImpl*>(layer); 31 32 layer_impl->SetLayout(image_aperture_, border_, fill_center_); 33 } 34 35 static gfx::RectF NormalizedRect(float x, 36 float y, 37 float width, 38 float height, 39 float total_width, 40 float total_height) { 41 return gfx::RectF(x / total_width, 42 y / total_height, 43 width / total_width, 44 height / total_height); 45 } 46 47 void NinePatchLayerImpl::SetLayout(gfx::Rect aperture, 48 gfx::Rect border, 49 bool fill_center) { 50 // This check imposes an ordering on the call sequence. An UIResource must 51 // exist before SetLayout can be called. 52 DCHECK(ui_resource_id_); 53 54 if (image_aperture_ == aperture && 55 border_ == border && fill_center_ == fill_center) 56 return; 57 58 image_aperture_ = aperture; 59 border_ = border; 60 fill_center_ = fill_center; 61 62 NoteLayerPropertyChanged(); 63 } 64 65 void NinePatchLayerImpl::CheckGeometryLimitations() { 66 // TODO(ccameron): the following "greater than or equal to" (GE) checks should 67 // be greater than (GT) to avoid degenerate nine-patches. The relaxed 68 // condition "equal to" is a workaround for the overhang shadow use case and 69 // should be investigated further. 70 71 // |border| is in layer space. It cannot exceed the bounds of the layer. 72 DCHECK(!border_.size().IsEmpty()); 73 DCHECK_GE(bounds().width(), border_.width()); 74 DCHECK_GE(bounds().height(), border_.height()); 75 76 // Sanity Check on |border| 77 DCHECK_LT(border_.x(), border_.width()); 78 DCHECK_LT(border_.y(), border_.height()); 79 DCHECK_GE(border_.x(), 0); 80 DCHECK_GE(border_.y(), 0); 81 82 // |aperture| is in image space. It cannot exceed the bounds of the bitmap. 83 DCHECK(!image_aperture_.size().IsEmpty()); 84 DCHECK(gfx::Rect(image_bounds_.width(), image_bounds_.height()) 85 .Contains(image_aperture_)); 86 87 // Avoid the degenerate cases where the aperture touches the edge of the 88 // image. 89 DCHECK_LT(image_aperture_.width(), image_bounds_.width() - 1); 90 DCHECK_LT(image_aperture_.height(), image_bounds_.height() - 1); 91 DCHECK_GT(image_aperture_.x(), 0); 92 DCHECK_GT(image_aperture_.y(), 0); 93 } 94 95 void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink, 96 AppendQuadsData* append_quads_data) { 97 CheckGeometryLimitations(); 98 SharedQuadState* shared_quad_state = 99 quad_sink->UseSharedQuadState(CreateSharedQuadState()); 100 AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data); 101 102 if (!ui_resource_id_) 103 return; 104 105 ResourceProvider::ResourceId resource = 106 layer_tree_impl()->ResourceIdForUIResource(ui_resource_id_); 107 108 if (!resource) 109 return; 110 111 static const bool flipped = false; 112 static const bool premultiplied_alpha = true; 113 114 DCHECK(!bounds().IsEmpty()); 115 116 // NinePatch border widths in layer space. 117 int layer_left_width = border_.x(); 118 int layer_top_height = border_.y(); 119 int layer_right_width = border_.width() - layer_left_width; 120 int layer_bottom_height = border_.height() - layer_top_height; 121 122 int layer_middle_width = bounds().width() - border_.width(); 123 int layer_middle_height = bounds().height() - border_.height(); 124 125 // Patch positions in layer space 126 gfx::Rect layer_top_left(0, 0, layer_left_width, layer_top_height); 127 gfx::Rect layer_top_right(bounds().width() - layer_right_width, 128 0, 129 layer_right_width, 130 layer_top_height); 131 gfx::Rect layer_bottom_left(0, 132 bounds().height() - layer_bottom_height, 133 layer_left_width, 134 layer_bottom_height); 135 gfx::Rect layer_bottom_right(layer_top_right.x(), 136 layer_bottom_left.y(), 137 layer_right_width, 138 layer_bottom_height); 139 gfx::Rect layer_top( 140 layer_top_left.right(), 0, layer_middle_width, layer_top_height); 141 gfx::Rect layer_left( 142 0, layer_top_left.bottom(), layer_left_width, layer_middle_height); 143 gfx::Rect layer_right(layer_top_right.x(), 144 layer_top_right.bottom(), 145 layer_right_width, 146 layer_left.height()); 147 gfx::Rect layer_bottom(layer_top.x(), 148 layer_bottom_left.y(), 149 layer_top.width(), 150 layer_bottom_height); 151 gfx::Rect layer_center(layer_left_width, 152 layer_top_height, 153 layer_middle_width, 154 layer_middle_height); 155 156 // Note the following values are in image (bitmap) space. 157 float image_width = image_bounds_.width(); 158 float image_height = image_bounds_.height(); 159 160 int image_aperture_left_width = image_aperture_.x(); 161 int image_aperture_top_height = image_aperture_.y(); 162 int image_aperture_right_width = image_width - image_aperture_.right(); 163 int image_aperture_bottom_height = image_height - image_aperture_.bottom(); 164 // Patch positions in bitmap UV space (from zero to one) 165 gfx::RectF uv_top_left = NormalizedRect(0, 166 0, 167 image_aperture_left_width, 168 image_aperture_top_height, 169 image_width, 170 image_height); 171 gfx::RectF uv_top_right = 172 NormalizedRect(image_width - image_aperture_right_width, 173 0, 174 image_aperture_right_width, 175 image_aperture_top_height, 176 image_width, 177 image_height); 178 gfx::RectF uv_bottom_left = 179 NormalizedRect(0, 180 image_height - image_aperture_bottom_height, 181 image_aperture_left_width, 182 image_aperture_bottom_height, 183 image_width, 184 image_height); 185 gfx::RectF uv_bottom_right = 186 NormalizedRect(image_width - image_aperture_right_width, 187 image_height - image_aperture_bottom_height, 188 image_aperture_right_width, 189 image_aperture_bottom_height, 190 image_width, 191 image_height); 192 gfx::RectF uv_top( 193 uv_top_left.right(), 194 0, 195 (image_width - image_aperture_left_width - image_aperture_right_width) / 196 image_width, 197 (image_aperture_top_height) / image_height); 198 gfx::RectF uv_left(0, 199 uv_top_left.bottom(), 200 image_aperture_left_width / image_width, 201 (image_height - image_aperture_top_height - 202 image_aperture_bottom_height) / 203 image_height); 204 gfx::RectF uv_right(uv_top_right.x(), 205 uv_top_right.bottom(), 206 image_aperture_right_width / image_width, 207 uv_left.height()); 208 gfx::RectF uv_bottom(uv_top.x(), 209 uv_bottom_left.y(), 210 uv_top.width(), 211 image_aperture_bottom_height / image_height); 212 gfx::RectF uv_center(uv_top_left.right(), 213 uv_top_left.bottom(), 214 uv_top.width(), 215 uv_left.height()); 216 217 // Nothing is opaque here. 218 // TODO(danakj): Should we look at the SkBitmaps to determine opaqueness? 219 gfx::Rect opaque_rect; 220 const float vertex_opacity[] = {1.0f, 1.0f, 1.0f, 1.0f}; 221 scoped_ptr<TextureDrawQuad> quad; 222 223 quad = TextureDrawQuad::Create(); 224 quad->SetNew(shared_quad_state, 225 layer_top_left, 226 opaque_rect, 227 resource, 228 premultiplied_alpha, 229 uv_top_left.origin(), 230 uv_top_left.bottom_right(), 231 SK_ColorTRANSPARENT, 232 vertex_opacity, 233 flipped); 234 quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); 235 236 quad = TextureDrawQuad::Create(); 237 quad->SetNew(shared_quad_state, 238 layer_top_right, 239 opaque_rect, 240 resource, 241 premultiplied_alpha, 242 uv_top_right.origin(), 243 uv_top_right.bottom_right(), 244 SK_ColorTRANSPARENT, 245 vertex_opacity, 246 flipped); 247 quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); 248 249 quad = TextureDrawQuad::Create(); 250 quad->SetNew(shared_quad_state, 251 layer_bottom_left, 252 opaque_rect, 253 resource, 254 premultiplied_alpha, 255 uv_bottom_left.origin(), 256 uv_bottom_left.bottom_right(), 257 SK_ColorTRANSPARENT, 258 vertex_opacity, 259 flipped); 260 quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); 261 262 quad = TextureDrawQuad::Create(); 263 quad->SetNew(shared_quad_state, 264 layer_bottom_right, 265 opaque_rect, 266 resource, 267 premultiplied_alpha, 268 uv_bottom_right.origin(), 269 uv_bottom_right.bottom_right(), 270 SK_ColorTRANSPARENT, 271 vertex_opacity, 272 flipped); 273 quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); 274 275 quad = TextureDrawQuad::Create(); 276 quad->SetNew(shared_quad_state, 277 layer_top, 278 opaque_rect, 279 resource, 280 premultiplied_alpha, 281 uv_top.origin(), 282 uv_top.bottom_right(), 283 SK_ColorTRANSPARENT, 284 vertex_opacity, 285 flipped); 286 quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); 287 288 quad = TextureDrawQuad::Create(); 289 quad->SetNew(shared_quad_state, 290 layer_left, 291 opaque_rect, 292 resource, 293 premultiplied_alpha, 294 uv_left.origin(), 295 uv_left.bottom_right(), 296 SK_ColorTRANSPARENT, 297 vertex_opacity, 298 flipped); 299 quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); 300 301 quad = TextureDrawQuad::Create(); 302 quad->SetNew(shared_quad_state, 303 layer_right, 304 opaque_rect, 305 resource, 306 premultiplied_alpha, 307 uv_right.origin(), 308 uv_right.bottom_right(), 309 SK_ColorTRANSPARENT, 310 vertex_opacity, 311 flipped); 312 quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); 313 314 quad = TextureDrawQuad::Create(); 315 quad->SetNew(shared_quad_state, 316 layer_bottom, 317 opaque_rect, 318 resource, 319 premultiplied_alpha, 320 uv_bottom.origin(), 321 uv_bottom.bottom_right(), 322 SK_ColorTRANSPARENT, 323 vertex_opacity, 324 flipped); 325 quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); 326 327 if (fill_center_) { 328 quad = TextureDrawQuad::Create(); 329 quad->SetNew(shared_quad_state, 330 layer_center, 331 opaque_rect, 332 resource, 333 premultiplied_alpha, 334 uv_center.origin(), 335 uv_center.bottom_right(), 336 SK_ColorTRANSPARENT, 337 vertex_opacity, 338 flipped); 339 quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); 340 } 341 } 342 343 const char* NinePatchLayerImpl::LayerTypeAsString() const { 344 return "cc::NinePatchLayerImpl"; 345 } 346 347 base::DictionaryValue* NinePatchLayerImpl::LayerTreeAsJson() const { 348 base::DictionaryValue* result = LayerImpl::LayerTreeAsJson(); 349 350 base::ListValue* list = new base::ListValue; 351 list->AppendInteger(image_aperture_.origin().x()); 352 list->AppendInteger(image_aperture_.origin().y()); 353 list->AppendInteger(image_aperture_.size().width()); 354 list->AppendInteger(image_aperture_.size().height()); 355 result->Set("ImageAperture", list); 356 357 list = new base::ListValue; 358 list->AppendInteger(image_bounds_.width()); 359 list->AppendInteger(image_bounds_.height()); 360 result->Set("ImageBounds", list); 361 362 result->Set("Border", MathUtil::AsValue(border_).release()); 363 364 base::FundamentalValue* fill_center = 365 base::Value::CreateBooleanValue(fill_center_); 366 result->Set("FillCenter", fill_center); 367 368 return result; 369 } 370 371 } // namespace cc 372