NinePatch.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. #include <cmath>
  2. #include "NinePatch.hpp"
  3. namespace gui3ds {
  4. NinePatch::NinePatch()
  5. : m_color(cpp3ds::Color::White)
  6. , m_padding(0,0,0,0)
  7. , m_needsUpdate(true)
  8. {
  9. setTexture(nullptr);
  10. }
  11. NinePatch::~NinePatch()
  12. {
  13. }
  14. void NinePatch::setTexture(const cpp3ds::Texture *texture)
  15. {
  16. m_texture = texture;
  17. updateRegions();
  18. m_needsUpdate = true;
  19. }
  20. const cpp3ds::Texture* NinePatch::getTexture() const
  21. {
  22. return m_texture;
  23. }
  24. void NinePatch::setContentSize(const cpp3ds::Vector2f &size) const
  25. {
  26. m_contentSize = size;
  27. m_needsUpdate = true;
  28. }
  29. void NinePatch::setContentSize(float width, float height) const
  30. {
  31. setContentSize(cpp3ds::Vector2f(width, height));
  32. }
  33. void NinePatch::setSize(const cpp3ds::Vector2f &size) const
  34. {
  35. m_contentSize.x = size.x + getPadding().width - getTexture()->getSize().x + 2.f;
  36. m_contentSize.y = size.y + getPadding().height - getTexture()->getSize().y + 2.f;
  37. m_needsUpdate = true;
  38. }
  39. void NinePatch::setSize(float width, float height) const
  40. {
  41. setSize(cpp3ds::Vector2f(width, height));
  42. }
  43. const cpp3ds::Vector2f &NinePatch::getContentSize() const
  44. {
  45. ensureUpdate();
  46. return m_contentSize;
  47. }
  48. cpp3ds::Vector2f NinePatch::getSize() const
  49. {
  50. ensureUpdate();
  51. return m_size;
  52. }
  53. void NinePatch::setPadding(const cpp3ds::FloatRect &padding)
  54. {
  55. m_needsUpdate = true;
  56. m_padding = padding;
  57. }
  58. void NinePatch::setPadding(float left, float top, float width, float height)
  59. {
  60. setPadding(cpp3ds::FloatRect(left, top, width, height));
  61. }
  62. const cpp3ds::FloatRect &NinePatch::getPadding() const
  63. {
  64. ensureUpdate();
  65. return m_padding;
  66. }
  67. void NinePatch::setColor(const cpp3ds::Color &color) const
  68. {
  69. m_color = color;
  70. m_needsUpdate = true;
  71. }
  72. const cpp3ds::Color &NinePatch::getColor() const
  73. {
  74. return m_color;
  75. }
  76. void NinePatch::draw(cpp3ds::RenderTarget &target, cpp3ds::RenderStates states) const
  77. {
  78. ensureUpdate();
  79. states.transform *= getTransform();
  80. states.texture = m_texture;
  81. target.draw(m_vertices, states);
  82. }
  83. void NinePatch::ensureUpdate() const
  84. {
  85. if (m_needsUpdate) {
  86. updateVertices();
  87. m_needsUpdate = false;
  88. }
  89. }
  90. void NinePatch::updateRegions() const
  91. {
  92. m_regions.clear();
  93. if (!m_texture)
  94. {
  95. Region region;
  96. region.type = DynamicBoth;
  97. region.rect = cpp3ds::FloatRect(0.f, 0.f, 1.f, 1.f);
  98. m_dynamicHeight = m_dynamicWidth = 1.f;
  99. m_staticHeight = m_staticWidth = 0.f;
  100. m_regions.push_back(region);
  101. return;
  102. }
  103. cpp3ds::Image image = m_texture->copyToImage();
  104. unsigned int x, y, last_x, last_y;
  105. bool is_static_y = true;
  106. bool is_static_x = true;
  107. last_x = 1;
  108. last_y = 1;
  109. m_staticHeight = 0;
  110. m_dynamicHeight = 0;
  111. for (y = 1; y < image.getSize().y - 1; y++)
  112. {
  113. cpp3ds::Color color_y = image.getPixel(0, y);
  114. bool is_region_y = is_static_y ? (color_y == cpp3ds::Color::Black) : (color_y.a == 0);
  115. if (y == image.getSize().y - 2) {
  116. is_region_y = true;
  117. y++;
  118. }
  119. if (is_region_y)
  120. {
  121. m_staticWidth = 0;
  122. m_dynamicWidth = 0;
  123. is_static_x = true;
  124. for (x = 1; x < image.getSize().x - 1; x++)
  125. {
  126. cpp3ds::Color color_x = image.getPixel(x, 0);
  127. bool is_region_x = is_static_x ? (color_x == cpp3ds::Color::Black) : (color_x.a == 0);
  128. if (x == image.getSize().x - 2) {
  129. is_region_x = true;
  130. x++;
  131. }
  132. if (is_region_x)
  133. {
  134. // add region
  135. Region region;
  136. if (is_static_x) {
  137. region.type = is_static_y ? Static : DynamicHeight;
  138. } else {
  139. region.type = is_static_y ? DynamicWidth : DynamicBoth;
  140. }
  141. region.rect.left = last_x;
  142. region.rect.top = last_y;
  143. region.rect.width = x - last_x;
  144. region.rect.height = y - last_y;
  145. m_regions.push_back(region);
  146. if (is_static_x)
  147. m_staticWidth += x - last_x;
  148. else
  149. m_dynamicWidth += x - last_x;
  150. last_x = x;
  151. is_static_x = !is_static_x;
  152. }
  153. }
  154. if (is_static_y)
  155. m_staticHeight += y - last_y;
  156. else
  157. m_dynamicHeight += y - last_y;
  158. last_x = 1;
  159. last_y = y;
  160. is_static_y = !is_static_y;
  161. }
  162. }
  163. // Get padding
  164. bool is_padding = false;
  165. for (y = 1; y < image.getSize().y - 1; y++)
  166. {
  167. cpp3ds::Color color = image.getPixel(image.getSize().x - 1, y);
  168. if (is_padding) {
  169. if (color.a == 0) {
  170. m_padding.height = y - 1 - m_padding.top;
  171. break;
  172. }
  173. } else {
  174. if (color == cpp3ds::Color::Black) {
  175. is_padding = true;
  176. m_padding.top = y - 1;
  177. continue;
  178. }
  179. }
  180. }
  181. is_padding = false;
  182. for (x = 1; x < image.getSize().x - 1; x++)
  183. {
  184. cpp3ds::Color color = image.getPixel(x, image.getSize().y - 1);
  185. if (is_padding) {
  186. if (color.a == 0) {
  187. m_padding.width = x - 1 - m_padding.left;
  188. break;
  189. }
  190. } else {
  191. if (color == cpp3ds::Color::Black) {
  192. is_padding = true;
  193. m_padding.left = x - 1;
  194. continue;
  195. }
  196. }
  197. }
  198. }
  199. void NinePatch::updateVertices() const
  200. {
  201. m_vertices.clear();
  202. cpp3ds::Vertex vertices[4];
  203. m_contentSize.x = std::max(m_contentSize.x, m_padding.width);
  204. m_contentSize.y = std::max(m_contentSize.y, m_padding.height);
  205. m_size.x = m_contentSize.x + m_texture->getSize().x - 2.f - m_padding.width;
  206. m_size.y = m_contentSize.y + m_texture->getSize().y - 2.f - m_padding.height;
  207. vertices[0].color = vertices[1].color = vertices[2].color = vertices[3].color = m_color;
  208. float dynamicWidthSum = 0;
  209. float dynamicHeightSum = 0;
  210. float dynamicWidth;
  211. float dynamicHeight;
  212. bool addedHeight = false;
  213. bool addedWidth = false;
  214. for (const auto &region: m_regions)
  215. {
  216. if (region.rect.left <= 1) {
  217. dynamicWidthSum = 0;
  218. }
  219. vertices[0].texCoords.x = region.rect.left;
  220. vertices[0].texCoords.y = region.rect.top;
  221. vertices[1].texCoords.x = region.rect.left + region.rect.width;
  222. vertices[1].texCoords.y = region.rect.top;
  223. vertices[2].texCoords.x = region.rect.left;
  224. vertices[2].texCoords.y = region.rect.top + region.rect.height;
  225. vertices[3].texCoords.x = region.rect.left + region.rect.width;
  226. vertices[3].texCoords.y = region.rect.top + region.rect.height;
  227. if (region.type == DynamicWidth || region.type == DynamicBoth) {
  228. dynamicWidth = std::floor((region.rect.width / m_dynamicWidth) * (m_contentSize.x - m_padding.width));
  229. if (!addedWidth) {
  230. addedWidth = true;
  231. dynamicWidthSum += dynamicWidth;
  232. }
  233. }
  234. if (region.type == DynamicHeight || region.type == DynamicBoth) {
  235. dynamicHeight = std::floor((region.rect.height / m_dynamicHeight) * (m_contentSize.y - m_padding.height));
  236. if (!addedHeight) {
  237. addedHeight = true;
  238. dynamicHeightSum += dynamicHeight;
  239. }
  240. }
  241. switch (region.type) {
  242. case DynamicWidth:
  243. addedHeight = false;
  244. vertices[0].position.x = region.rect.left + dynamicWidthSum - dynamicWidth - 1;
  245. vertices[0].position.y = region.rect.top + dynamicHeightSum - 1;
  246. vertices[1].position.x = region.rect.left + region.rect.width + dynamicWidthSum - 1;
  247. vertices[1].position.y = region.rect.top + dynamicHeightSum - 1;
  248. vertices[2].position.x = region.rect.left + dynamicWidthSum - dynamicWidth - 1;
  249. vertices[2].position.y = region.rect.top + region.rect.height + dynamicHeightSum - 1;
  250. break;
  251. case DynamicHeight:
  252. addedWidth = false;
  253. vertices[0].position.x = region.rect.left + dynamicWidthSum - 1;
  254. vertices[0].position.y = region.rect.top + dynamicHeightSum - dynamicHeight - 1;
  255. vertices[1].position.x = region.rect.left + region.rect.width + dynamicWidthSum - 1;
  256. vertices[1].position.y = region.rect.top + dynamicHeightSum - dynamicHeight - 1;
  257. vertices[2].position.x = region.rect.left + dynamicWidthSum - 1;
  258. vertices[2].position.y = region.rect.top + region.rect.height + dynamicHeightSum - 1;
  259. break;
  260. case DynamicBoth:
  261. vertices[0].position.x = region.rect.left + dynamicWidthSum - dynamicWidth - 1;
  262. vertices[0].position.y = region.rect.top + dynamicHeightSum - dynamicHeight - 1;
  263. vertices[1].position.x = region.rect.left + region.rect.width + dynamicWidthSum - 1;
  264. vertices[1].position.y = region.rect.top + dynamicHeightSum - dynamicHeight - 1;
  265. vertices[2].position.x = region.rect.left + dynamicWidthSum - dynamicWidth - 1;
  266. vertices[2].position.y = region.rect.top + region.rect.height + dynamicHeightSum - 1;
  267. break;
  268. case Static:
  269. default:
  270. addedWidth = false;
  271. addedHeight = false;
  272. vertices[0].position = cpp3ds::Vector2f(region.rect.left+dynamicWidthSum-1, region.rect.top+dynamicHeightSum-1);
  273. vertices[1].position = cpp3ds::Vector2f(region.rect.left+dynamicWidthSum-1 + region.rect.width, region.rect.top+dynamicHeightSum-1);
  274. vertices[2].position = cpp3ds::Vector2f(region.rect.left+dynamicWidthSum-1, region.rect.top+dynamicHeightSum-1 + region.rect.height);
  275. break;
  276. }
  277. // Bottom-right position is the same for all region rectangles
  278. vertices[3].position.x = region.rect.left + region.rect.width + dynamicWidthSum - 1;
  279. vertices[3].position.y = region.rect.top + region.rect.height + dynamicHeightSum - 1;
  280. // Add vertices counter-clockwise as two triangles
  281. m_vertices.append(vertices[0]);
  282. m_vertices.append(vertices[2]);
  283. m_vertices.append(vertices[3]);
  284. m_vertices.append(vertices[1]);
  285. m_vertices.append(vertices[0]);
  286. m_vertices.append(vertices[3]);
  287. }
  288. }
  289. } // namespace gui3ds