nine_patch_layer_impl.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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
15namespace cc {
16
17NinePatchLayerImpl::NinePatchLayerImpl(LayerTreeImpl* tree_impl, int id)
18    : UIResourceLayerImpl(tree_impl, id),
19      fill_center_(false) {}
20
21NinePatchLayerImpl::~NinePatchLayerImpl() {}
22
23scoped_ptr<LayerImpl> NinePatchLayerImpl::CreateLayerImpl(
24    LayerTreeImpl* tree_impl) {
25  return NinePatchLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>();
26}
27
28void 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
35static 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
47void 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
65void 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
95void 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
343const char* NinePatchLayerImpl::LayerTypeAsString() const {
344  return "cc::NinePatchLayerImpl";
345}
346
347base::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