nine_patch_layer_impl.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
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/layers/quad_sink.h"
10#include "cc/quads/texture_draw_quad.h"
11#include "ui/gfx/rect_f.h"
12
13namespace cc {
14
15NinePatchLayerImpl::NinePatchLayerImpl(LayerTreeImpl* tree_impl, int id)
16    : LayerImpl(tree_impl, id),
17      resource_id_(0) {}
18
19NinePatchLayerImpl::~NinePatchLayerImpl() {}
20
21ResourceProvider::ResourceId NinePatchLayerImpl::ContentsResourceId() const {
22  return 0;
23}
24
25scoped_ptr<LayerImpl> NinePatchLayerImpl::CreateLayerImpl(
26    LayerTreeImpl* tree_impl) {
27  return NinePatchLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>();
28}
29
30void NinePatchLayerImpl::PushPropertiesTo(LayerImpl* layer) {
31  LayerImpl::PushPropertiesTo(layer);
32  NinePatchLayerImpl* layer_impl = static_cast<NinePatchLayerImpl*>(layer);
33
34  if (!resource_id_)
35    return;
36
37  layer_impl->SetResourceId(resource_id_);
38  layer_impl->SetLayout(image_bounds_, image_aperture_);
39}
40
41static gfx::RectF NormalizedRect(float x,
42                                 float y,
43                                 float width,
44                                 float height,
45                                 float total_width,
46                                 float total_height) {
47  return gfx::RectF(x / total_width,
48                    y / total_height,
49                    width / total_width,
50                    height / total_height);
51}
52
53void NinePatchLayerImpl::SetLayout(gfx::Size image_bounds, gfx::Rect aperture) {
54  image_bounds_ = image_bounds;
55  image_aperture_ = aperture;
56}
57
58bool NinePatchLayerImpl::WillDraw(DrawMode draw_mode,
59                                  ResourceProvider* resource_provider) {
60  if (!resource_id_ || draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE)
61    return false;
62  return LayerImpl::WillDraw(draw_mode, resource_provider);
63}
64
65void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink,
66                                     AppendQuadsData* append_quads_data) {
67  SharedQuadState* shared_quad_state =
68      quad_sink->UseSharedQuadState(CreateSharedQuadState());
69  AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data);
70
71  if (!resource_id_)
72    return;
73
74  static const bool flipped = false;
75  static const bool premultiplied_alpha = true;
76
77  DCHECK(!bounds().IsEmpty());
78
79  // NinePatch border widths in bitmap pixel space
80  int left_width = image_aperture_.x();
81  int top_height = image_aperture_.y();
82  int right_width = image_bounds_.width() - image_aperture_.right();
83  int bottom_height = image_bounds_.height() - image_aperture_.bottom();
84
85  // If layer can't fit the corners, clip to show the outer edges of the
86  // image.
87  int corner_total_width = left_width + right_width;
88  int middle_width = bounds().width() - corner_total_width;
89  if (middle_width < 0) {
90    float left_width_proportion =
91        static_cast<float>(left_width) / corner_total_width;
92    int left_width_crop = middle_width * left_width_proportion;
93    left_width += left_width_crop;
94    right_width = bounds().width() - left_width;
95    middle_width = 0;
96  }
97  int corner_total_height = top_height + bottom_height;
98  int middle_height = bounds().height() - corner_total_height;
99  if (middle_height < 0) {
100    float top_height_proportion =
101        static_cast<float>(top_height) / corner_total_height;
102    int top_height_crop = middle_height * top_height_proportion;
103    top_height += top_height_crop;
104    bottom_height = bounds().height() - top_height;
105    middle_height = 0;
106  }
107
108  // Patch positions in layer space
109  gfx::Rect top_left(0, 0, left_width, top_height);
110  gfx::Rect top_right(
111      bounds().width() - right_width, 0, right_width, top_height);
112  gfx::Rect bottom_left(
113      0, bounds().height() - bottom_height, left_width, bottom_height);
114  gfx::Rect bottom_right(
115      top_right.x(), bottom_left.y(), right_width, bottom_height);
116  gfx::Rect top(top_left.right(), 0, middle_width, top_height);
117  gfx::Rect left(0, top_left.bottom(), left_width, middle_height);
118  gfx::Rect right(top_right.x(),
119                  top_right.bottom(),
120                  right_width,
121                  left.height());
122  gfx::Rect bottom(top.x(), bottom_left.y(), top.width(), bottom_height);
123
124  float img_width = image_bounds_.width();
125  float img_height = image_bounds_.height();
126
127  // Patch positions in bitmap UV space (from zero to one)
128  gfx::RectF uv_top_left = NormalizedRect(0,
129                                          0,
130                                          left_width,
131                                          top_height,
132                                          img_width,
133                                          img_height);
134  gfx::RectF uv_top_right = NormalizedRect(img_width - right_width,
135                                           0,
136                                           right_width,
137                                           top_height,
138                                           img_width,
139                                           img_height);
140  gfx::RectF uv_bottom_left = NormalizedRect(0,
141                                             img_height - bottom_height,
142                                             left_width,
143                                             bottom_height,
144                                             img_width,
145                                             img_height);
146  gfx::RectF uv_bottom_right = NormalizedRect(img_width - right_width,
147                                              img_height - bottom_height,
148                                              right_width,
149                                              bottom_height,
150                                              img_width,
151                                              img_height);
152  gfx::RectF uv_top(uv_top_left.right(),
153                   0,
154                   (img_width - left_width - right_width) / img_width,
155                   (top_height) / img_height);
156  gfx::RectF uv_left(0,
157                    uv_top_left.bottom(),
158                    left_width / img_width,
159                    (img_height - top_height - bottom_height) / img_height);
160  gfx::RectF uv_right(uv_top_right.x(),
161                     uv_top_right.bottom(),
162                     right_width / img_width,
163                     uv_left.height());
164  gfx::RectF uv_bottom(uv_top.x(),
165                      uv_bottom_left.y(),
166                      uv_top.width(),
167                      bottom_height / img_height);
168
169  // Nothing is opaque here.
170  // TODO(danakj): Should we look at the SkBitmaps to determine opaqueness?
171  gfx::Rect opaque_rect;
172  const float vertex_opacity[] = {1.0f, 1.0f, 1.0f, 1.0f};
173  scoped_ptr<TextureDrawQuad> quad;
174
175  quad = TextureDrawQuad::Create();
176  quad->SetNew(shared_quad_state,
177               top_left,
178               opaque_rect,
179               resource_id_,
180               premultiplied_alpha,
181               uv_top_left.origin(),
182               uv_top_left.bottom_right(),
183               SK_ColorTRANSPARENT,
184               vertex_opacity,
185               flipped);
186  quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
187
188  quad = TextureDrawQuad::Create();
189  quad->SetNew(shared_quad_state,
190               top_right,
191               opaque_rect,
192               resource_id_,
193               premultiplied_alpha,
194               uv_top_right.origin(),
195               uv_top_right.bottom_right(),
196               SK_ColorTRANSPARENT,
197               vertex_opacity,
198               flipped);
199  quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
200
201  quad = TextureDrawQuad::Create();
202  quad->SetNew(shared_quad_state,
203               bottom_left,
204               opaque_rect,
205               resource_id_,
206               premultiplied_alpha,
207               uv_bottom_left.origin(),
208               uv_bottom_left.bottom_right(),
209               SK_ColorTRANSPARENT,
210               vertex_opacity,
211               flipped);
212  quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
213
214  quad = TextureDrawQuad::Create();
215  quad->SetNew(shared_quad_state,
216               bottom_right,
217               opaque_rect,
218               resource_id_,
219               premultiplied_alpha,
220               uv_bottom_right.origin(),
221               uv_bottom_right.bottom_right(),
222               SK_ColorTRANSPARENT,
223               vertex_opacity,
224               flipped);
225  quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
226
227  quad = TextureDrawQuad::Create();
228  quad->SetNew(shared_quad_state,
229               top,
230               opaque_rect,
231               resource_id_,
232               premultiplied_alpha,
233               uv_top.origin(),
234               uv_top.bottom_right(),
235               SK_ColorTRANSPARENT,
236               vertex_opacity,
237               flipped);
238  quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
239
240  quad = TextureDrawQuad::Create();
241  quad->SetNew(shared_quad_state,
242               left,
243               opaque_rect,
244               resource_id_,
245               premultiplied_alpha,
246               uv_left.origin(),
247               uv_left.bottom_right(),
248               SK_ColorTRANSPARENT,
249               vertex_opacity,
250               flipped);
251  quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
252
253  quad = TextureDrawQuad::Create();
254  quad->SetNew(shared_quad_state,
255               right,
256               opaque_rect,
257               resource_id_,
258               premultiplied_alpha,
259               uv_right.origin(),
260               uv_right.bottom_right(),
261               SK_ColorTRANSPARENT,
262               vertex_opacity,
263               flipped);
264  quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
265
266  quad = TextureDrawQuad::Create();
267  quad->SetNew(shared_quad_state,
268               bottom,
269               opaque_rect,
270               resource_id_,
271               premultiplied_alpha,
272               uv_bottom.origin(),
273               uv_bottom.bottom_right(),
274               SK_ColorTRANSPARENT,
275               vertex_opacity,
276               flipped);
277  quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
278}
279
280void NinePatchLayerImpl::DidLoseOutputSurface() {
281  resource_id_ = 0;
282}
283
284const char* NinePatchLayerImpl::LayerTypeAsString() const {
285  return "cc::NinePatchLayerImpl";
286}
287
288base::DictionaryValue* NinePatchLayerImpl::LayerTreeAsJson() const {
289  base::DictionaryValue* result = LayerImpl::LayerTreeAsJson();
290
291  base::ListValue* list = new base::ListValue;
292  list->AppendInteger(image_aperture_.origin().x());
293  list->AppendInteger(image_aperture_.origin().y());
294  list->AppendInteger(image_aperture_.size().width());
295  list->AppendInteger(image_aperture_.size().height());
296  result->Set("ImageAperture", list);
297
298  list = new base::ListValue;
299  list->AppendInteger(image_bounds_.width());
300  list->AppendInteger(image_bounds_.height());
301  result->Set("ImageBounds", list);
302
303  return result;
304}
305
306}  // namespace cc
307