123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- #include <cmath>
- #include "NinePatch.hpp"
- namespace gui3ds {
- NinePatch::NinePatch()
- : m_color(cpp3ds::Color::White)
- , m_padding(0,0,0,0)
- , m_needsUpdate(true)
- {
- setTexture(nullptr);
- }
- NinePatch::~NinePatch()
- {
- }
- void NinePatch::setTexture(const cpp3ds::Texture *texture)
- {
- m_texture = texture;
- updateRegions();
- m_needsUpdate = true;
- }
- const cpp3ds::Texture* NinePatch::getTexture() const
- {
- return m_texture;
- }
- void NinePatch::setContentSize(const cpp3ds::Vector2f &size) const
- {
- m_contentSize = size;
- m_needsUpdate = true;
- }
- void NinePatch::setContentSize(float width, float height) const
- {
- setContentSize(cpp3ds::Vector2f(width, height));
- }
- void NinePatch::setSize(const cpp3ds::Vector2f &size) const
- {
- m_contentSize.x = size.x + getPadding().width - getTexture()->getSize().x + 2.f;
- m_contentSize.y = size.y + getPadding().height - getTexture()->getSize().y + 2.f;
- m_needsUpdate = true;
- }
- void NinePatch::setSize(float width, float height) const
- {
- setSize(cpp3ds::Vector2f(width, height));
- }
- const cpp3ds::Vector2f &NinePatch::getContentSize() const
- {
- ensureUpdate();
- return m_contentSize;
- }
- cpp3ds::Vector2f NinePatch::getSize() const
- {
- ensureUpdate();
- return m_size;
- }
- void NinePatch::setPadding(const cpp3ds::FloatRect &padding)
- {
- m_needsUpdate = true;
- m_padding = padding;
- }
- void NinePatch::setPadding(float left, float top, float width, float height)
- {
- setPadding(cpp3ds::FloatRect(left, top, width, height));
- }
- const cpp3ds::FloatRect &NinePatch::getPadding() const
- {
- ensureUpdate();
- return m_padding;
- }
- void NinePatch::setColor(const cpp3ds::Color &color) const
- {
- m_color = color;
- m_needsUpdate = true;
- }
- const cpp3ds::Color &NinePatch::getColor() const
- {
- return m_color;
- }
- void NinePatch::draw(cpp3ds::RenderTarget &target, cpp3ds::RenderStates states) const
- {
- ensureUpdate();
- states.transform *= getTransform();
- states.texture = m_texture;
- target.draw(m_vertices, states);
- }
- void NinePatch::ensureUpdate() const
- {
- if (m_needsUpdate) {
- updateVertices();
- m_needsUpdate = false;
- }
- }
- void NinePatch::updateRegions() const
- {
- m_regions.clear();
- if (!m_texture)
- {
- Region region;
- region.type = DynamicBoth;
- region.rect = cpp3ds::FloatRect(0.f, 0.f, 1.f, 1.f);
- m_dynamicHeight = m_dynamicWidth = 1.f;
- m_staticHeight = m_staticWidth = 0.f;
- m_regions.push_back(region);
- return;
- }
- cpp3ds::Image image = m_texture->copyToImage();
- unsigned int x, y, last_x, last_y;
- bool is_static_y = true;
- bool is_static_x = true;
- last_x = 1;
- last_y = 1;
- m_staticHeight = 0;
- m_dynamicHeight = 0;
- for (y = 1; y < image.getSize().y - 1; y++)
- {
- cpp3ds::Color color_y = image.getPixel(0, y);
- bool is_region_y = is_static_y ? (color_y == cpp3ds::Color::Black) : (color_y.a == 0);
- if (y == image.getSize().y - 2) {
- is_region_y = true;
- y++;
- }
- if (is_region_y)
- {
- m_staticWidth = 0;
- m_dynamicWidth = 0;
- is_static_x = true;
- for (x = 1; x < image.getSize().x - 1; x++)
- {
- cpp3ds::Color color_x = image.getPixel(x, 0);
- bool is_region_x = is_static_x ? (color_x == cpp3ds::Color::Black) : (color_x.a == 0);
- if (x == image.getSize().x - 2) {
- is_region_x = true;
- x++;
- }
- if (is_region_x)
- {
- // add region
- Region region;
- if (is_static_x) {
- region.type = is_static_y ? Static : DynamicHeight;
- } else {
- region.type = is_static_y ? DynamicWidth : DynamicBoth;
- }
- region.rect.left = last_x;
- region.rect.top = last_y;
- region.rect.width = x - last_x;
- region.rect.height = y - last_y;
- m_regions.push_back(region);
- if (is_static_x)
- m_staticWidth += x - last_x;
- else
- m_dynamicWidth += x - last_x;
- last_x = x;
- is_static_x = !is_static_x;
- }
- }
- if (is_static_y)
- m_staticHeight += y - last_y;
- else
- m_dynamicHeight += y - last_y;
- last_x = 1;
- last_y = y;
- is_static_y = !is_static_y;
- }
- }
- // Get padding
- bool is_padding = false;
- for (y = 1; y < image.getSize().y - 1; y++)
- {
- cpp3ds::Color color = image.getPixel(image.getSize().x - 1, y);
- if (is_padding) {
- if (color.a == 0) {
- m_padding.height = y - 1 - m_padding.top;
- break;
- }
- } else {
- if (color == cpp3ds::Color::Black) {
- is_padding = true;
- m_padding.top = y - 1;
- continue;
- }
- }
- }
- is_padding = false;
- for (x = 1; x < image.getSize().x - 1; x++)
- {
- cpp3ds::Color color = image.getPixel(x, image.getSize().y - 1);
- if (is_padding) {
- if (color.a == 0) {
- m_padding.width = x - 1 - m_padding.left;
- break;
- }
- } else {
- if (color == cpp3ds::Color::Black) {
- is_padding = true;
- m_padding.left = x - 1;
- continue;
- }
- }
- }
- }
- void NinePatch::updateVertices() const
- {
- m_vertices.clear();
- cpp3ds::Vertex vertices[4];
- m_contentSize.x = std::max(m_contentSize.x, m_padding.width);
- m_contentSize.y = std::max(m_contentSize.y, m_padding.height);
- m_size.x = m_contentSize.x + m_texture->getSize().x - 2.f - m_padding.width;
- m_size.y = m_contentSize.y + m_texture->getSize().y - 2.f - m_padding.height;
- vertices[0].color = vertices[1].color = vertices[2].color = vertices[3].color = m_color;
- float dynamicWidthSum = 0;
- float dynamicHeightSum = 0;
- float dynamicWidth;
- float dynamicHeight;
- bool addedHeight = false;
- bool addedWidth = false;
- for (const auto ®ion: m_regions)
- {
- if (region.rect.left <= 1) {
- dynamicWidthSum = 0;
- }
- vertices[0].texCoords.x = region.rect.left;
- vertices[0].texCoords.y = region.rect.top;
- vertices[1].texCoords.x = region.rect.left + region.rect.width;
- vertices[1].texCoords.y = region.rect.top;
- vertices[2].texCoords.x = region.rect.left;
- vertices[2].texCoords.y = region.rect.top + region.rect.height;
- vertices[3].texCoords.x = region.rect.left + region.rect.width;
- vertices[3].texCoords.y = region.rect.top + region.rect.height;
- if (region.type == DynamicWidth || region.type == DynamicBoth) {
- dynamicWidth = std::floor((region.rect.width / m_dynamicWidth) * (m_contentSize.x - m_padding.width));
- if (!addedWidth) {
- addedWidth = true;
- dynamicWidthSum += dynamicWidth;
- }
- }
- if (region.type == DynamicHeight || region.type == DynamicBoth) {
- dynamicHeight = std::floor((region.rect.height / m_dynamicHeight) * (m_contentSize.y - m_padding.height));
- if (!addedHeight) {
- addedHeight = true;
- dynamicHeightSum += dynamicHeight;
- }
- }
- switch (region.type) {
- case DynamicWidth:
- addedHeight = false;
- vertices[0].position.x = region.rect.left + dynamicWidthSum - dynamicWidth - 1;
- vertices[0].position.y = region.rect.top + dynamicHeightSum - 1;
- vertices[1].position.x = region.rect.left + region.rect.width + dynamicWidthSum - 1;
- vertices[1].position.y = region.rect.top + dynamicHeightSum - 1;
- vertices[2].position.x = region.rect.left + dynamicWidthSum - dynamicWidth - 1;
- vertices[2].position.y = region.rect.top + region.rect.height + dynamicHeightSum - 1;
- break;
- case DynamicHeight:
- addedWidth = false;
- vertices[0].position.x = region.rect.left + dynamicWidthSum - 1;
- vertices[0].position.y = region.rect.top + dynamicHeightSum - dynamicHeight - 1;
- vertices[1].position.x = region.rect.left + region.rect.width + dynamicWidthSum - 1;
- vertices[1].position.y = region.rect.top + dynamicHeightSum - dynamicHeight - 1;
- vertices[2].position.x = region.rect.left + dynamicWidthSum - 1;
- vertices[2].position.y = region.rect.top + region.rect.height + dynamicHeightSum - 1;
- break;
- case DynamicBoth:
- vertices[0].position.x = region.rect.left + dynamicWidthSum - dynamicWidth - 1;
- vertices[0].position.y = region.rect.top + dynamicHeightSum - dynamicHeight - 1;
- vertices[1].position.x = region.rect.left + region.rect.width + dynamicWidthSum - 1;
- vertices[1].position.y = region.rect.top + dynamicHeightSum - dynamicHeight - 1;
- vertices[2].position.x = region.rect.left + dynamicWidthSum - dynamicWidth - 1;
- vertices[2].position.y = region.rect.top + region.rect.height + dynamicHeightSum - 1;
- break;
- case Static:
- default:
- addedWidth = false;
- addedHeight = false;
- vertices[0].position = cpp3ds::Vector2f(region.rect.left+dynamicWidthSum-1, region.rect.top+dynamicHeightSum-1);
- vertices[1].position = cpp3ds::Vector2f(region.rect.left+dynamicWidthSum-1 + region.rect.width, region.rect.top+dynamicHeightSum-1);
- vertices[2].position = cpp3ds::Vector2f(region.rect.left+dynamicWidthSum-1, region.rect.top+dynamicHeightSum-1 + region.rect.height);
- break;
- }
- // Bottom-right position is the same for all region rectangles
- vertices[3].position.x = region.rect.left + region.rect.width + dynamicWidthSum - 1;
- vertices[3].position.y = region.rect.top + region.rect.height + dynamicHeightSum - 1;
- // Add vertices counter-clockwise as two triangles
- m_vertices.append(vertices[0]);
- m_vertices.append(vertices[2]);
- m_vertices.append(vertices[3]);
- m_vertices.append(vertices[1]);
- m_vertices.append(vertices[0]);
- m_vertices.append(vertices[3]);
- }
- }
- } // namespace gui3ds
|