blob: 7677f48bd110df12a9e6f9c69d18860b24010875 [file] [log] [blame]
bokane7a058a2017-03-02 22:42:511// Copyright 2016 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
danakj83d0eb4f2017-09-19 17:31:585#include "cc/layers/nine_patch_generator.h"
bokane7a058a2017-03-02 22:42:516
bokane7a058a2017-03-02 22:42:517#include "cc/trees/layer_tree_impl.h"
danakje5805be2017-09-15 19:24:558#include "components/viz/common/quads/render_pass.h"
9#include "components/viz/common/quads/texture_draw_quad.h"
bokane7a058a2017-03-02 22:42:5110#include "ui/gfx/geometry/rect_conversions.h"
11#include "ui/gfx/geometry/rect_f.h"
12
13namespace cc {
14
15namespace {
16
17// Maximum number of patches that can be produced for one NinePatchLayer.
18const int kMaxOcclusionPatches = 12;
19const int kMaxPatches = 9;
20
21gfx::RectF BoundsToRect(int x1, int y1, int x2, int y2) {
22 return gfx::RectF(x1, y1, x2 - x1, y2 - y1);
23}
24
25gfx::RectF NormalizedRect(const gfx::RectF& rect,
26 float total_width,
27 float total_height) {
28 return gfx::RectF(rect.x() / total_width, rect.y() / total_height,
29 rect.width() / total_width, rect.height() / total_height);
30}
31
32} // namespace
33
34NinePatchGenerator::Patch::Patch(const gfx::RectF& image_rect,
35 const gfx::Size& total_image_bounds,
36 const gfx::RectF& output_rect)
37 : image_rect(image_rect),
38 normalized_image_rect(NormalizedRect(image_rect,
39 total_image_bounds.width(),
40 total_image_bounds.height())),
41 output_rect(output_rect) {}
42
43NinePatchGenerator::NinePatchGenerator()
44 : fill_center_(false), nearest_neighbor_(false) {}
45
46bool NinePatchGenerator::SetLayout(const gfx::Size& image_bounds,
47 const gfx::Size& output_bounds,
48 const gfx::Rect& aperture,
49 const gfx::Rect& border,
50 const gfx::Rect& output_occlusion,
51 bool fill_center,
52 bool nearest_neighbor) {
53 if (image_bounds_ == image_bounds && output_bounds_ == output_bounds &&
54 image_aperture_ == aperture && border_ == border &&
55 fill_center_ == fill_center && output_occlusion_ == output_occlusion &&
56 nearest_neighbor_ == nearest_neighbor)
57 return false;
58
59 image_bounds_ = image_bounds;
60 output_bounds_ = output_bounds;
61 image_aperture_ = aperture;
62 border_ = border;
63 fill_center_ = fill_center;
64 output_occlusion_ = output_occlusion;
65 nearest_neighbor_ = nearest_neighbor;
66
67 return true;
68}
69
70void NinePatchGenerator::CheckGeometryLimitations() {
71 // |border| is in layer space. It cannot exceed the bounds of the layer.
72 DCHECK_GE(output_bounds_.width(), border_.width());
73 DCHECK_GE(output_bounds_.height(), border_.height());
74
75 // Sanity Check on |border|
76 DCHECK_LE(border_.x(), border_.width());
77 DCHECK_LE(border_.y(), border_.height());
78 DCHECK_GE(border_.x(), 0);
79 DCHECK_GE(border_.y(), 0);
80
81 // |aperture| is in image space. It cannot exceed the bounds of the bitmap.
82 DCHECK(!image_aperture_.size().IsEmpty());
83 DCHECK(gfx::Rect(image_bounds_).Contains(image_aperture_))
84 << "image_bounds_ " << gfx::Rect(image_bounds_).ToString()
85 << " image_aperture_ " << image_aperture_.ToString();
86
87 // Sanity check on |output_occlusion_|. It should always be within the
88 // border.
89 gfx::Rect border_rect(border_.x(), border_.y(),
90 output_bounds_.width() - border_.width(),
91 output_bounds_.height() - border_.height());
92 DCHECK(output_occlusion_.IsEmpty() || output_occlusion_.Contains(border_rect))
93 << "border_rect " << border_rect.ToString() << " output_occlusion_ "
94 << output_occlusion_.ToString();
95}
96
97std::vector<NinePatchGenerator::Patch>
98NinePatchGenerator::ComputeQuadsWithoutOcclusion() const {
99 float image_width = image_bounds_.width();
100 float image_height = image_bounds_.height();
101 float output_width = output_bounds_.width();
102 float output_height = output_bounds_.height();
103 gfx::RectF output_aperture(border_.x(), border_.y(),
104 output_width - border_.width(),
105 output_height - border_.height());
106
107 std::vector<Patch> patches;
108 patches.reserve(kMaxPatches);
109
110 // Top-left.
111 patches.push_back(
112 Patch(BoundsToRect(0, 0, image_aperture_.x(), image_aperture_.y()),
113 image_bounds_,
114 BoundsToRect(0, 0, output_aperture.x(), output_aperture.y())));
115
116 // Top-right.
117 patches.push_back(Patch(BoundsToRect(image_aperture_.right(), 0, image_width,
118 image_aperture_.y()),
119 image_bounds_,
120 BoundsToRect(output_aperture.right(), 0, output_width,
121 output_aperture.y())));
122
123 // Bottom-left.
124 patches.push_back(Patch(BoundsToRect(0, image_aperture_.bottom(),
125 image_aperture_.x(), image_height),
126 image_bounds_,
127 BoundsToRect(0, output_aperture.bottom(),
128 output_aperture.x(), output_height)));
129
130 // Bottom-right.
131 patches.push_back(
132 Patch(BoundsToRect(image_aperture_.right(), image_aperture_.bottom(),
133 image_width, image_height),
134 image_bounds_,
135 BoundsToRect(output_aperture.right(), output_aperture.bottom(),
136 output_width, output_height)));
137
138 // Top.
139 patches.push_back(
140 Patch(BoundsToRect(image_aperture_.x(), 0, image_aperture_.right(),
141 image_aperture_.y()),
142 image_bounds_,
143 BoundsToRect(output_aperture.x(), 0, output_aperture.right(),
144 output_aperture.y())));
145
146 // Left.
147 patches.push_back(
148 Patch(BoundsToRect(0, image_aperture_.y(), image_aperture_.x(),
149 image_aperture_.bottom()),
150 image_bounds_,
151 BoundsToRect(0, output_aperture.y(), output_aperture.x(),
152 output_aperture.bottom())));
153
154 // Right.
155 patches.push_back(
156 Patch(BoundsToRect(image_aperture_.right(), image_aperture_.y(),
157 image_width, image_aperture_.bottom()),
158 image_bounds_,
159 BoundsToRect(output_aperture.right(), output_aperture.y(),
160 output_width, output_aperture.bottom())));
161
162 // Bottom.
163 patches.push_back(
164 Patch(BoundsToRect(image_aperture_.x(), image_aperture_.bottom(),
165 image_aperture_.right(), image_height),
166 image_bounds_,
167 BoundsToRect(output_aperture.x(), output_aperture.bottom(),
168 output_aperture.right(), output_height)));
169
170 // Center.
171 if (fill_center_) {
172 patches.push_back(
173 Patch(BoundsToRect(image_aperture_.x(), image_aperture_.y(),
174 image_aperture_.right(), image_aperture_.bottom()),
175 image_bounds_,
176 BoundsToRect(output_aperture.x(), output_aperture.y(),
177 output_aperture.right(), output_aperture.bottom())));
178 }
179
180 return patches;
181}
182
183std::vector<NinePatchGenerator::Patch>
184NinePatchGenerator::ComputeQuadsWithOcclusion() const {
185 float image_width = image_bounds_.width();
186 float image_height = image_bounds_.height();
187
188 float output_width = output_bounds_.width();
189 float output_height = output_bounds_.height();
190
191 float layer_border_right = border_.width() - border_.x();
192 float layer_border_bottom = border_.height() - border_.y();
193
194 float image_aperture_right = image_width - image_aperture_.right();
195 float image_aperture_bottom = image_height - image_aperture_.bottom();
196
197 float output_occlusion_right = output_width - output_occlusion_.right();
198 float output_occlusion_bottom = output_height - output_occlusion_.bottom();
199
200 gfx::RectF image_occlusion(BoundsToRect(
201 border_.x() == 0
202 ? 0
203 : (output_occlusion_.x() * image_aperture_.x() / border_.x()),
204 border_.y() == 0
205 ? 0
206 : (output_occlusion_.y() * image_aperture_.y() / border_.y()),
207 image_width - (layer_border_right == 0
208 ? 0
209 : output_occlusion_right * image_aperture_right /
210 layer_border_right),
211 image_height - (layer_border_bottom == 0
212 ? 0
213 : output_occlusion_bottom * image_aperture_bottom /
214 layer_border_bottom)));
215 gfx::RectF output_aperture(border_.x(), border_.y(),
216 output_width - border_.width(),
217 output_height - border_.height());
218
219 std::vector<Patch> patches;
220 patches.reserve(kMaxOcclusionPatches);
221
222 // Top-left-left.
223 patches.push_back(
224 Patch(BoundsToRect(0, 0, image_occlusion.x(), image_aperture_.y()),
225 image_bounds_,
226 BoundsToRect(0, 0, output_occlusion_.x(), output_aperture.y())));
227
228 // Top-left-right.
229 patches.push_back(
230 Patch(BoundsToRect(image_occlusion.x(), 0, image_aperture_.x(),
231 image_occlusion.y()),
232 image_bounds_,
233 BoundsToRect(output_occlusion_.x(), 0, output_aperture.x(),
234 output_occlusion_.y())));
235
236 // Top-center.
237 patches.push_back(
238 Patch(BoundsToRect(image_aperture_.x(), 0, image_aperture_.right(),
239 image_occlusion.y()),
240 image_bounds_,
241 BoundsToRect(output_aperture.x(), 0, output_aperture.right(),
242 output_occlusion_.y())));
243
244 // Top-right-left.
245 patches.push_back(
246 Patch(BoundsToRect(image_aperture_.right(), 0, image_occlusion.right(),
247 image_occlusion.y()),
248 image_bounds_,
249 BoundsToRect(output_aperture.right(), 0, output_occlusion_.right(),
250 output_occlusion_.y())));
251
252 // Top-right-right.
253 patches.push_back(Patch(BoundsToRect(image_occlusion.right(), 0, image_width,
254 image_aperture_.y()),
255 image_bounds_,
256 BoundsToRect(output_occlusion_.right(), 0,
257 output_width, output_aperture.y())));
258
259 // Left-center.
260 patches.push_back(
261 Patch(BoundsToRect(0, image_aperture_.y(), image_occlusion.x(),
262 image_aperture_.bottom()),
263 image_bounds_,
264 BoundsToRect(0, output_aperture.y(), output_occlusion_.x(),
265 output_aperture.bottom())));
266
267 // Right-center.
268 patches.push_back(
269 Patch(BoundsToRect(image_occlusion.right(), image_aperture_.y(),
270 image_width, image_aperture_.bottom()),
271 image_bounds_,
272 BoundsToRect(output_occlusion_.right(), output_aperture.y(),
273 output_width, output_aperture.bottom())));
274
275 // Bottom-left-left.
276 patches.push_back(Patch(BoundsToRect(0, image_aperture_.bottom(),
277 image_occlusion.x(), image_height),
278 image_bounds_,
279 BoundsToRect(0, output_aperture.bottom(),
280 output_occlusion_.x(), output_height)));
281
282 // Bottom-left-right.
283 patches.push_back(
284 Patch(BoundsToRect(image_occlusion.x(), image_occlusion.bottom(),
285 image_aperture_.x(), image_height),
286 image_bounds_,
287 BoundsToRect(output_occlusion_.x(), output_occlusion_.bottom(),
288 output_aperture.x(), output_height)));
289
290 // Bottom-center.
291 patches.push_back(
292 Patch(BoundsToRect(image_aperture_.x(), image_occlusion.bottom(),
293 image_aperture_.right(), image_height),
294 image_bounds_,
295 BoundsToRect(output_aperture.x(), output_occlusion_.bottom(),
296 output_aperture.right(), output_height)));
297
298 // Bottom-right-left.
299 patches.push_back(
300 Patch(BoundsToRect(image_aperture_.right(), image_occlusion.bottom(),
301 image_occlusion.right(), image_height),
302 image_bounds_,
303 BoundsToRect(output_aperture.right(), output_occlusion_.bottom(),
304 output_occlusion_.right(), output_height)));
305
306 // Bottom-right-right.
307 patches.push_back(
308 Patch(BoundsToRect(image_occlusion.right(), image_aperture_.bottom(),
309 image_width, image_height),
310 image_bounds_,
311 BoundsToRect(output_occlusion_.right(), output_aperture.bottom(),
312 output_width, output_height)));
313
314 return patches;
315}
316
317std::vector<NinePatchGenerator::Patch> NinePatchGenerator::GeneratePatches()
318 const {
319 DCHECK(!output_bounds_.IsEmpty());
320
321 std::vector<Patch> patches;
322
323 if (output_occlusion_.IsEmpty() || fill_center_)
324 patches = ComputeQuadsWithoutOcclusion();
325 else
326 patches = ComputeQuadsWithOcclusion();
327
328 return patches;
329}
330
331void NinePatchGenerator::AppendQuads(LayerImpl* layer_impl,
332 UIResourceId ui_resource_id,
danakje5805be2017-09-15 19:24:55333 viz::RenderPass* render_pass,
Alex Zhangabad2292017-08-23 21:55:19334 viz::SharedQuadState* shared_quad_state,
bokane7a058a2017-03-02 22:42:51335 const std::vector<Patch>& patches) {
336 if (!ui_resource_id)
337 return;
338
Fady Samuelc80a4a862017-07-28 10:23:36339 viz::ResourceId resource =
bokane7a058a2017-03-02 22:42:51340 layer_impl->layer_tree_impl()->ResourceIdForUIResource(ui_resource_id);
341
342 if (!resource)
343 return;
344
345 const float vertex_opacity[] = {1.0f, 1.0f, 1.0f, 1.0f};
346 const bool opaque =
347 layer_impl->layer_tree_impl()->IsUIResourceOpaque(ui_resource_id);
348 constexpr bool flipped = false;
349 constexpr bool premultiplied_alpha = true;
350
351 for (const auto& patch : patches) {
352 gfx::Rect output_rect = gfx::ToEnclosingRect(patch.output_rect);
353 gfx::Rect visible_rect =
354 layer_impl->draw_properties()
355 .occlusion_in_content_space.GetUnoccludedContentRect(output_rect);
yiyix78ccdd92017-08-29 03:59:46356 bool needs_blending = !opaque;
bokane7a058a2017-03-02 22:42:51357 if (!visible_rect.IsEmpty()) {
358 gfx::RectF image_rect = patch.normalized_image_rect;
danakje5805be2017-09-15 19:24:55359 auto* quad = render_pass->CreateAndAppendDrawQuad<viz::TextureDrawQuad>();
yiyix78ccdd92017-08-29 03:59:46360 quad->SetNew(shared_quad_state, output_rect, visible_rect, needs_blending,
361 resource, premultiplied_alpha, image_rect.origin(),
362 image_rect.bottom_right(), SK_ColorTRANSPARENT,
Daniel Nicoara3a0e00b82018-11-27 19:22:52363 vertex_opacity, flipped, nearest_neighbor_,
364 /*secure_output_only=*/false,
365 ui::ProtectedVideoType::kClear);
bokane7a058a2017-03-02 22:42:51366 layer_impl->ValidateQuadResources(quad);
367 }
368 }
369}
370
371void NinePatchGenerator::AsJson(base::DictionaryValue* dictionary) const {
Jeremy Roman909d927b2017-08-27 18:34:09372 auto list = std::make_unique<base::ListValue>();
bokane7a058a2017-03-02 22:42:51373 list->AppendInteger(image_aperture_.origin().x());
374 list->AppendInteger(image_aperture_.origin().y());
375 list->AppendInteger(image_aperture_.size().width());
376 list->AppendInteger(image_aperture_.size().height());
vabrbce355c2017-03-23 18:52:43377 dictionary->Set("ImageAperture", std::move(list));
bokane7a058a2017-03-02 22:42:51378
Jeremy Roman909d927b2017-08-27 18:34:09379 list = std::make_unique<base::ListValue>();
bokane7a058a2017-03-02 22:42:51380 list->AppendInteger(image_bounds_.width());
381 list->AppendInteger(image_bounds_.height());
vabrbce355c2017-03-23 18:52:43382 dictionary->Set("ImageBounds", std::move(list));
bokane7a058a2017-03-02 22:42:51383
vabrbce355c2017-03-23 18:52:43384 dictionary->Set("Border", MathUtil::AsValue(border_));
bokane7a058a2017-03-02 22:42:51385
386 dictionary->SetBoolean("FillCenter", fill_center_);
387
Jeremy Roman909d927b2017-08-27 18:34:09388 list = std::make_unique<base::ListValue>();
bokane7a058a2017-03-02 22:42:51389 list->AppendInteger(output_occlusion_.x());
390 list->AppendInteger(output_occlusion_.y());
391 list->AppendInteger(output_occlusion_.width());
392 list->AppendInteger(output_occlusion_.height());
vabrbce355c2017-03-23 18:52:43393 dictionary->Set("OutputOcclusion", std::move(list));
bokane7a058a2017-03-02 22:42:51394}
395
396} // namespace cc