diff --git a/drape/drape_common.pri b/drape/drape_common.pri index 3b1d585d09..3f150243c9 100644 --- a/drape/drape_common.pri +++ b/drape/drape_common.pri @@ -35,6 +35,7 @@ SOURCES += \ $$DRAPE_DIR/texture_set_holder.cpp \ $$DRAPE_DIR/utils/stb_image.c \ $$DRAPE_DIR/attribute_buffer_mutator.cpp \ + $$DRAPE_DIR/stipple_pen_resource.cpp HEADERS += \ $$DRAPE_DIR/data_buffer.hpp \ @@ -76,3 +77,4 @@ HEADERS += \ $$DRAPE_DIR/attribute_buffer_mutator.hpp \ $$DRAPE_DIR/drape_global.hpp \ $$DRAPE_DIR/object_pool.hpp \ + $$DRAPE_DIR/stipple_pen_resource.hpp diff --git a/drape/drape_tests/drape_tests.pro b/drape/drape_tests/drape_tests.pro index 5d5e0c8f02..26e1b79da2 100644 --- a/drape/drape_tests/drape_tests.pro +++ b/drape/drape_tests/drape_tests.pro @@ -39,6 +39,7 @@ SOURCES += \ pointers_tests.cpp \ font_texture_tests.cpp \ bingind_info_tests.cpp \ + stipple_pen_tests.cpp HEADERS += \ glmock_functions.hpp \ diff --git a/drape/drape_tests/stipple_pen_tests.cpp b/drape/drape_tests/stipple_pen_tests.cpp new file mode 100644 index 0000000000..53d2fa2bab --- /dev/null +++ b/drape/drape_tests/stipple_pen_tests.cpp @@ -0,0 +1,82 @@ +#include "../../testing/testing.hpp" + +#include "../stipple_pen_resource.hpp" + +using namespace dp; + +namespace +{ + void TestPacker(StipplePenPacker & packer, uint32_t width, m2::RectU const & expect) + { + m2::RectU rect = packer.PackResource(width); + TEST_EQUAL(rect, expect, ()); + } +} + +UNIT_TEST(SimpleStipplePackTest) +{ + StipplePenPacker packer(m2::PointU(1024, 8)); + TestPacker(packer, 30, m2::RectU(1, 1, 31, 2)); + TestPacker(packer, 254, m2::RectU(1, 3, 255, 4)); + TestPacker(packer, 1, m2::RectU(1, 5, 2, 6)); + TestPacker(packer, 250, m2::RectU(256, 1, 506, 2)); + TestPacker(packer, 249, m2::RectU(256, 3, 505, 4)); +} + +UNIT_TEST(SimpleStippleTest) +{ + StipplePenKey key; + TEST_EQUAL(key.Tag, StipplePenTag, ()); + key.m_pattern.push_back(12); + key.m_pattern.push_back(12); + key.m_pattern.push_back(8); + key.m_pattern.push_back(9); + + StipplePenResource res(key); + TEST_EQUAL(res.GetSize(), 246, ()); + TEST_EQUAL(res.GetBufferSize(), 246, ()); + + uint8_t buffer[250]; + memset(buffer, 127, ARRAY_SIZE(buffer) * sizeof(uint8_t)); + res.Rasterize(buffer + 2); + + typedef pair TRange; + typedef pair TCheckNode; + + TCheckNode nodes[38] = + { + make_pair(make_pair(0, 2), 127), + make_pair(make_pair(2, 14), 255), + make_pair(make_pair(14, 26), 0), + make_pair(make_pair(26, 34), 255), + make_pair(make_pair(34, 43), 0), + make_pair(make_pair(43, 55), 255), + make_pair(make_pair(55, 67), 0), + make_pair(make_pair(67, 75), 255), + make_pair(make_pair(75, 84), 0), + make_pair(make_pair(84, 96), 255), + make_pair(make_pair(96, 108), 0), + make_pair(make_pair(108, 116), 255), + make_pair(make_pair(116, 125), 0), + make_pair(make_pair(125, 137), 255), + make_pair(make_pair(137, 149), 0), + make_pair(make_pair(149, 157), 255), + make_pair(make_pair(157, 166), 0), + make_pair(make_pair(166, 178), 255), + make_pair(make_pair(178, 190), 0), + make_pair(make_pair(190, 198), 255), + make_pair(make_pair(198, 207), 0), + make_pair(make_pair(207, 219), 255), + make_pair(make_pair(219, 231), 0), + make_pair(make_pair(231, 239), 255), + make_pair(make_pair(239, 248), 0), + make_pair(make_pair(248, 250), 127), + }; + + for (size_t i = 0; i < ARRAY_SIZE(nodes); ++i) + { + TCheckNode const & node = nodes[i]; + for (size_t r = node.first.first; r < node.first.second; ++r) + TEST_EQUAL(buffer[r], node.second, (r)); + } +} diff --git a/drape/stipple_pen_resource.cpp b/drape/stipple_pen_resource.cpp new file mode 100644 index 0000000000..266076901d --- /dev/null +++ b/drape/stipple_pen_resource.cpp @@ -0,0 +1,91 @@ +#include "stipple_pen_resource.hpp" + +#include "../std/numeric.hpp" + +namespace dp +{ + +uint32_t const MAX_STIPPLE_PEN_LENGTH = 254; +uint32_t const COLUMN_WIDTH = MAX_STIPPLE_PEN_LENGTH + 1; + +StipplePenPacker::StipplePenPacker(m2::PointU const & canvasSize) + : m_canvasSize(canvasSize) + , m_currentColumn(0) +{ + // canvasSize.x - 1 = we reserve 1 pixel border on left + // to reduce problems with bilinear filtration on GPU + // MAX_STIPPLE_PEN_LENGTH + 1 = we reserve 1 pixel empty space after pen resource + // pen in last column reserve 1 pixel on right size of canvas + uint32_t columnCount = floor((canvasSize.x - 1) / MAX_STIPPLE_PEN_LENGTH); + m_columns.resize(columnCount, 0); +} + +m2::RectU StipplePenPacker::PackResource(uint32_t width) +{ + ASSERT(m_currentColumn < m_columns.size(), ()); + uint32_t countInColumn = m_columns[m_currentColumn]; + // on one pattern we reserve 2 pixels. 1 pixel for pattern + // and 1 pixel for empty space beetween patterns + // also we reserve 1 pixel border on top of canvas + uint32_t yOffset = countInColumn * 2 + 1; + // ASSERT that ne pattern can be packed in current column + ASSERT(yOffset + 2 < m_canvasSize.y, ()); + ++m_columns[m_currentColumn]; + // 1 + m_currentColumn = reserve 1 pixel border on left side + uint32_t xOffset = 1 + m_currentColumn * COLUMN_WIDTH; + // we check if new pattern can be mapped in this column + // yOffset + 4 = 2 pixels on current pattern and 2 for new pattern + if (yOffset + 4 > m_canvasSize.y) + m_currentColumn++; + + // maxY = yOffset + 1 because real height of stipple pattern is 1 + return m2::RectU(xOffset, yOffset, xOffset + width, yOffset + 1); +} + +StipplePenResource::StipplePenResource(StipplePenKey const & key) + : m_key(key) +{ + uint32_t fullPattern = accumulate(m_key.m_pattern.begin(), m_key.m_pattern.end(), 0); + ASSERT(fullPattern < MAX_STIPPLE_PEN_LENGTH, ()); + uint32_t count = floor(MAX_STIPPLE_PEN_LENGTH / fullPattern); + m_pixelLength = count * fullPattern; +} + +uint32_t StipplePenResource::GetSize() const +{ + return m_pixelLength; +} + +uint32_t StipplePenResource::GetBufferSize() const +{ + return m_pixelLength; +} + +TextureFormat StipplePenResource::GetExpectedFormat() const +{ + return ALPHA; +} + +void StipplePenResource::Rasterize(void * buffer) +{ + uint8_t * pixels = static_cast(buffer); + uint16_t offset = 0; + for (size_t i = 0; i < m_key.m_pattern.size(); ++i) + { + uint8_t value = (i & 0x1) == 0 ? 255 : 0; + uint8_t length = m_key.m_pattern[i]; + memset(pixels + offset, value, length * sizeof(uint8_t)); + offset += length; + } + + uint8_t period = offset; + + while (offset < m_pixelLength) + { + memcpy(pixels + offset, pixels, period); + offset += period; + } +} + +} + diff --git a/drape/stipple_pen_resource.hpp b/drape/stipple_pen_resource.hpp new file mode 100644 index 0000000000..e1be5045f3 --- /dev/null +++ b/drape/stipple_pen_resource.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include "drape_global.hpp" + +#include "../base/buffer_vector.hpp" + +#include "../geometry/point2d.hpp" +#include "../geometry/rect2d.hpp" + +namespace dp +{ + +class StipplePenPacker +{ +public: + StipplePenPacker(m2::PointU const & canvasSize); + + m2::RectU PackResource(uint32_t width); + +private: + m2::PointU m_canvasSize; + buffer_vector m_columns; + uint32_t m_currentColumn; +}; + +struct StipplePenKey +{ + enum { Tag = StipplePenTag }; + + buffer_vector m_pattern; +}; + +class StipplePenResource +{ +public: + StipplePenResource(StipplePenKey const & key); + + uint32_t GetSize() const; + uint32_t GetBufferSize() const; + TextureFormat GetExpectedFormat() const; + + void Rasterize(void * buffer); + +private: + StipplePenKey m_key; + uint32_t m_pixelLength; +}; + +}