nine_patch_generator.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. // 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. #include "cc/layers/nine_patch_generator.h"
  5. #include "base/trace_event/traced_value.h"
  6. #include "cc/base/math_util.h"
  7. #include "cc/trees/layer_tree_impl.h"
  8. #include "components/viz/common/quads/compositor_render_pass.h"
  9. #include "components/viz/common/quads/texture_draw_quad.h"
  10. #include "ui/gfx/geometry/rect_conversions.h"
  11. #include "ui/gfx/geometry/rect_f.h"
  12. namespace cc {
  13. namespace {
  14. // Maximum number of patches that can be produced for one NinePatchLayer.
  15. const int kMaxOcclusionPatches = 12;
  16. const int kMaxPatches = 9;
  17. gfx::RectF BoundsToRect(int x1, int y1, int x2, int y2) {
  18. return gfx::RectF(x1, y1, x2 - x1, y2 - y1);
  19. }
  20. gfx::RectF NormalizedRect(const gfx::RectF& rect,
  21. float total_width,
  22. float total_height) {
  23. return gfx::RectF(rect.x() / total_width, rect.y() / total_height,
  24. rect.width() / total_width, rect.height() / total_height);
  25. }
  26. } // namespace
  27. NinePatchGenerator::Patch::Patch(const gfx::RectF& image_rect,
  28. const gfx::Size& total_image_bounds,
  29. const gfx::RectF& output_rect)
  30. : image_rect(image_rect),
  31. normalized_image_rect(NormalizedRect(image_rect,
  32. total_image_bounds.width(),
  33. total_image_bounds.height())),
  34. output_rect(output_rect) {}
  35. NinePatchGenerator::NinePatchGenerator()
  36. : fill_center_(false), nearest_neighbor_(false) {}
  37. bool NinePatchGenerator::SetLayout(const gfx::Size& image_bounds,
  38. const gfx::Size& output_bounds,
  39. const gfx::Rect& aperture,
  40. const gfx::Rect& border,
  41. const gfx::Rect& output_occlusion,
  42. bool fill_center,
  43. bool nearest_neighbor) {
  44. if (image_bounds_ == image_bounds && output_bounds_ == output_bounds &&
  45. image_aperture_ == aperture && border_ == border &&
  46. fill_center_ == fill_center && output_occlusion_ == output_occlusion &&
  47. nearest_neighbor_ == nearest_neighbor)
  48. return false;
  49. image_bounds_ = image_bounds;
  50. output_bounds_ = output_bounds;
  51. image_aperture_ = aperture;
  52. border_ = border;
  53. fill_center_ = fill_center;
  54. output_occlusion_ = output_occlusion;
  55. nearest_neighbor_ = nearest_neighbor;
  56. return true;
  57. }
  58. void NinePatchGenerator::CheckGeometryLimitations() {
  59. // |border| is in layer space. It cannot exceed the bounds of the layer.
  60. DCHECK_GE(output_bounds_.width(), border_.width());
  61. DCHECK_GE(output_bounds_.height(), border_.height());
  62. // Sanity Check on |border|
  63. DCHECK_LE(border_.x(), border_.width());
  64. DCHECK_LE(border_.y(), border_.height());
  65. DCHECK_GE(border_.x(), 0);
  66. DCHECK_GE(border_.y(), 0);
  67. // |aperture| is in image space. It cannot exceed the bounds of the bitmap.
  68. DCHECK(!image_aperture_.size().IsEmpty());
  69. DCHECK(gfx::Rect(image_bounds_).Contains(image_aperture_))
  70. << "image_bounds_ " << gfx::Rect(image_bounds_).ToString()
  71. << " image_aperture_ " << image_aperture_.ToString();
  72. // Sanity check on |output_occlusion_|. It should always be within the
  73. // border.
  74. gfx::Rect border_rect(border_.x(), border_.y(),
  75. output_bounds_.width() - border_.width(),
  76. output_bounds_.height() - border_.height());
  77. DCHECK(output_occlusion_.IsEmpty() || output_occlusion_.Contains(border_rect))
  78. << "border_rect " << border_rect.ToString() << " output_occlusion_ "
  79. << output_occlusion_.ToString();
  80. }
  81. std::vector<NinePatchGenerator::Patch>
  82. NinePatchGenerator::ComputeQuadsWithoutOcclusion() const {
  83. float image_width = image_bounds_.width();
  84. float image_height = image_bounds_.height();
  85. float output_width = output_bounds_.width();
  86. float output_height = output_bounds_.height();
  87. gfx::RectF output_aperture(border_.x(), border_.y(),
  88. output_width - border_.width(),
  89. output_height - border_.height());
  90. std::vector<Patch> patches;
  91. patches.reserve(kMaxPatches);
  92. // Top-left.
  93. patches.push_back(
  94. Patch(BoundsToRect(0, 0, image_aperture_.x(), image_aperture_.y()),
  95. image_bounds_,
  96. BoundsToRect(0, 0, output_aperture.x(), output_aperture.y())));
  97. // Top-right.
  98. patches.push_back(Patch(BoundsToRect(image_aperture_.right(), 0, image_width,
  99. image_aperture_.y()),
  100. image_bounds_,
  101. BoundsToRect(output_aperture.right(), 0, output_width,
  102. output_aperture.y())));
  103. // Bottom-left.
  104. patches.push_back(Patch(BoundsToRect(0, image_aperture_.bottom(),
  105. image_aperture_.x(), image_height),
  106. image_bounds_,
  107. BoundsToRect(0, output_aperture.bottom(),
  108. output_aperture.x(), output_height)));
  109. // Bottom-right.
  110. patches.push_back(
  111. Patch(BoundsToRect(image_aperture_.right(), image_aperture_.bottom(),
  112. image_width, image_height),
  113. image_bounds_,
  114. BoundsToRect(output_aperture.right(), output_aperture.bottom(),
  115. output_width, output_height)));
  116. // Top.
  117. patches.push_back(
  118. Patch(BoundsToRect(image_aperture_.x(), 0, image_aperture_.right(),
  119. image_aperture_.y()),
  120. image_bounds_,
  121. BoundsToRect(output_aperture.x(), 0, output_aperture.right(),
  122. output_aperture.y())));
  123. // Left.
  124. patches.push_back(
  125. Patch(BoundsToRect(0, image_aperture_.y(), image_aperture_.x(),
  126. image_aperture_.bottom()),
  127. image_bounds_,
  128. BoundsToRect(0, output_aperture.y(), output_aperture.x(),
  129. output_aperture.bottom())));
  130. // Right.
  131. patches.push_back(
  132. Patch(BoundsToRect(image_aperture_.right(), image_aperture_.y(),
  133. image_width, image_aperture_.bottom()),
  134. image_bounds_,
  135. BoundsToRect(output_aperture.right(), output_aperture.y(),
  136. output_width, output_aperture.bottom())));
  137. // Bottom.
  138. patches.push_back(
  139. Patch(BoundsToRect(image_aperture_.x(), image_aperture_.bottom(),
  140. image_aperture_.right(), image_height),
  141. image_bounds_,
  142. BoundsToRect(output_aperture.x(), output_aperture.bottom(),
  143. output_aperture.right(), output_height)));
  144. // Center.
  145. if (fill_center_) {
  146. patches.push_back(
  147. Patch(BoundsToRect(image_aperture_.x(), image_aperture_.y(),
  148. image_aperture_.right(), image_aperture_.bottom()),
  149. image_bounds_,
  150. BoundsToRect(output_aperture.x(), output_aperture.y(),
  151. output_aperture.right(), output_aperture.bottom())));
  152. }
  153. return patches;
  154. }
  155. std::vector<NinePatchGenerator::Patch>
  156. NinePatchGenerator::ComputeQuadsWithOcclusion() const {
  157. float image_width = image_bounds_.width();
  158. float image_height = image_bounds_.height();
  159. float output_width = output_bounds_.width();
  160. float output_height = output_bounds_.height();
  161. float layer_border_right = border_.width() - border_.x();
  162. float layer_border_bottom = border_.height() - border_.y();
  163. float image_aperture_right = image_width - image_aperture_.right();
  164. float image_aperture_bottom = image_height - image_aperture_.bottom();
  165. float output_occlusion_right = output_width - output_occlusion_.right();
  166. float output_occlusion_bottom = output_height - output_occlusion_.bottom();
  167. gfx::RectF image_occlusion(BoundsToRect(
  168. border_.x() == 0
  169. ? 0
  170. : (output_occlusion_.x() * image_aperture_.x() / border_.x()),
  171. border_.y() == 0
  172. ? 0
  173. : (output_occlusion_.y() * image_aperture_.y() / border_.y()),
  174. image_width - (layer_border_right == 0
  175. ? 0
  176. : output_occlusion_right * image_aperture_right /
  177. layer_border_right),
  178. image_height - (layer_border_bottom == 0
  179. ? 0
  180. : output_occlusion_bottom * image_aperture_bottom /
  181. layer_border_bottom)));
  182. gfx::RectF output_aperture(border_.x(), border_.y(),
  183. output_width - border_.width(),
  184. output_height - border_.height());
  185. std::vector<Patch> patches;
  186. patches.reserve(kMaxOcclusionPatches);
  187. // Top-left-left.
  188. patches.push_back(
  189. Patch(BoundsToRect(0, 0, image_occlusion.x(), image_aperture_.y()),
  190. image_bounds_,
  191. BoundsToRect(0, 0, output_occlusion_.x(), output_aperture.y())));
  192. // Top-left-right.
  193. patches.push_back(
  194. Patch(BoundsToRect(image_occlusion.x(), 0, image_aperture_.x(),
  195. image_occlusion.y()),
  196. image_bounds_,
  197. BoundsToRect(output_occlusion_.x(), 0, output_aperture.x(),
  198. output_occlusion_.y())));
  199. // Top-center.
  200. patches.push_back(
  201. Patch(BoundsToRect(image_aperture_.x(), 0, image_aperture_.right(),
  202. image_occlusion.y()),
  203. image_bounds_,
  204. BoundsToRect(output_aperture.x(), 0, output_aperture.right(),
  205. output_occlusion_.y())));
  206. // Top-right-left.
  207. patches.push_back(
  208. Patch(BoundsToRect(image_aperture_.right(), 0, image_occlusion.right(),
  209. image_occlusion.y()),
  210. image_bounds_,
  211. BoundsToRect(output_aperture.right(), 0, output_occlusion_.right(),
  212. output_occlusion_.y())));
  213. // Top-right-right.
  214. patches.push_back(Patch(BoundsToRect(image_occlusion.right(), 0, image_width,
  215. image_aperture_.y()),
  216. image_bounds_,
  217. BoundsToRect(output_occlusion_.right(), 0,
  218. output_width, output_aperture.y())));
  219. // Left-center.
  220. patches.push_back(
  221. Patch(BoundsToRect(0, image_aperture_.y(), image_occlusion.x(),
  222. image_aperture_.bottom()),
  223. image_bounds_,
  224. BoundsToRect(0, output_aperture.y(), output_occlusion_.x(),
  225. output_aperture.bottom())));
  226. // Right-center.
  227. patches.push_back(
  228. Patch(BoundsToRect(image_occlusion.right(), image_aperture_.y(),
  229. image_width, image_aperture_.bottom()),
  230. image_bounds_,
  231. BoundsToRect(output_occlusion_.right(), output_aperture.y(),
  232. output_width, output_aperture.bottom())));
  233. // Bottom-left-left.
  234. patches.push_back(Patch(BoundsToRect(0, image_aperture_.bottom(),
  235. image_occlusion.x(), image_height),
  236. image_bounds_,
  237. BoundsToRect(0, output_aperture.bottom(),
  238. output_occlusion_.x(), output_height)));
  239. // Bottom-left-right.
  240. patches.push_back(
  241. Patch(BoundsToRect(image_occlusion.x(), image_occlusion.bottom(),
  242. image_aperture_.x(), image_height),
  243. image_bounds_,
  244. BoundsToRect(output_occlusion_.x(), output_occlusion_.bottom(),
  245. output_aperture.x(), output_height)));
  246. // Bottom-center.
  247. patches.push_back(
  248. Patch(BoundsToRect(image_aperture_.x(), image_occlusion.bottom(),
  249. image_aperture_.right(), image_height),
  250. image_bounds_,
  251. BoundsToRect(output_aperture.x(), output_occlusion_.bottom(),
  252. output_aperture.right(), output_height)));
  253. // Bottom-right-left.
  254. patches.push_back(
  255. Patch(BoundsToRect(image_aperture_.right(), image_occlusion.bottom(),
  256. image_occlusion.right(), image_height),
  257. image_bounds_,
  258. BoundsToRect(output_aperture.right(), output_occlusion_.bottom(),
  259. output_occlusion_.right(), output_height)));
  260. // Bottom-right-right.
  261. patches.push_back(
  262. Patch(BoundsToRect(image_occlusion.right(), image_aperture_.bottom(),
  263. image_width, image_height),
  264. image_bounds_,
  265. BoundsToRect(output_occlusion_.right(), output_aperture.bottom(),
  266. output_width, output_height)));
  267. return patches;
  268. }
  269. std::vector<NinePatchGenerator::Patch> NinePatchGenerator::GeneratePatches()
  270. const {
  271. DCHECK(!output_bounds_.IsEmpty());
  272. std::vector<Patch> patches;
  273. if (output_occlusion_.IsEmpty() || fill_center_)
  274. patches = ComputeQuadsWithoutOcclusion();
  275. else
  276. patches = ComputeQuadsWithOcclusion();
  277. return patches;
  278. }
  279. void NinePatchGenerator::AppendQuads(LayerImpl* layer_impl,
  280. UIResourceId ui_resource_id,
  281. viz::CompositorRenderPass* render_pass,
  282. viz::SharedQuadState* shared_quad_state,
  283. const std::vector<Patch>& patches) {
  284. if (!ui_resource_id)
  285. return;
  286. viz::ResourceId resource =
  287. layer_impl->layer_tree_impl()->ResourceIdForUIResource(ui_resource_id);
  288. if (!resource)
  289. return;
  290. const float vertex_opacity[] = {1.0f, 1.0f, 1.0f, 1.0f};
  291. const bool opaque =
  292. layer_impl->layer_tree_impl()->IsUIResourceOpaque(ui_resource_id);
  293. constexpr bool flipped = false;
  294. constexpr bool premultiplied_alpha = true;
  295. for (const auto& patch : patches) {
  296. gfx::Rect output_rect = gfx::ToEnclosingRect(patch.output_rect);
  297. gfx::Rect visible_rect =
  298. layer_impl->draw_properties()
  299. .occlusion_in_content_space.GetUnoccludedContentRect(output_rect);
  300. bool needs_blending = !opaque;
  301. if (!visible_rect.IsEmpty()) {
  302. gfx::RectF image_rect = patch.normalized_image_rect;
  303. auto* quad = render_pass->CreateAndAppendDrawQuad<viz::TextureDrawQuad>();
  304. quad->SetNew(shared_quad_state, output_rect, visible_rect, needs_blending,
  305. resource, premultiplied_alpha, image_rect.origin(),
  306. image_rect.bottom_right(), SkColors::kTransparent,
  307. vertex_opacity, flipped, nearest_neighbor_,
  308. /*secure_output_only=*/false,
  309. gfx::ProtectedVideoType::kClear);
  310. layer_impl->ValidateQuadResources(quad);
  311. }
  312. }
  313. }
  314. void NinePatchGenerator::AsValueInto(
  315. base::trace_event::TracedValue* state) const {
  316. MathUtil::AddToTracedValue("ImageAperture", image_aperture_, state);
  317. MathUtil::AddToTracedValue("ImageBounds", image_bounds_, state);
  318. MathUtil::AddToTracedValue("Border", border_, state);
  319. state->SetBoolean("FillCenter", fill_center_);
  320. MathUtil::AddToTracedValue("OutputOcclusion", output_occlusion_, state);
  321. }
  322. } // namespace cc