From b6347fb194eea1cac45e61bd7dc4864b47ed39eb Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sun, 16 Jun 2024 17:34:23 +0100 Subject: [PATCH] clx_render: Further simplify outline rendering 1. Switches to uint8_t for coordinate storage. 2. Avoids producing duplicate pixels. --- Source/engine/render/clx_render.cpp | 109 +++++++++++++++------------ Source/utils/algorithm/container.hpp | 7 ++ Source/utils/static_vector.hpp | 51 ++++--------- 3 files changed, 83 insertions(+), 84 deletions(-) diff --git a/Source/engine/render/clx_render.cpp b/Source/engine/render/clx_render.cpp index aadbf6b57..95a3db750 100644 --- a/Source/engine/render/clx_render.cpp +++ b/Source/engine/render/clx_render.cpp @@ -6,15 +6,11 @@ #include "clx_render.hpp" #include -#include -#include #include "engine/point.hpp" #include "engine/render/blit_impl.hpp" -#include "engine/render/scrollrt.h" #include "utils/attributes.h" #include "utils/clx_decode.hpp" -#include "utils/static_bit_vector.hpp" #include "utils/static_vector.hpp" #ifdef DEBUG_CLX @@ -255,9 +251,9 @@ void DoRenderBackwards( } constexpr size_t MaxOutlinePixels = 1024; -constexpr size_t MaxOutlineSpriteWidth = 256; -using OutlinePixels = StaticVector, MaxOutlinePixels>; -using OutlineRowSolidRuns = StaticVector, MaxOutlineSpriteWidth / 2 + 1>; +constexpr size_t MaxOutlineSpriteWidth = 253; +using OutlinePixels = StaticVector, MaxOutlinePixels>; +using OutlineRowSolidRuns = StaticVector, MaxOutlineSpriteWidth / 2 + 1>; struct OutlinePixelsCacheEntry { OutlinePixels outlinePixels; @@ -267,31 +263,41 @@ struct OutlinePixelsCacheEntry { OutlinePixelsCacheEntry OutlinePixelsCache; void PopulateOutlinePixelsForRow( - const StaticBitVector &rowBelow, - const StaticBitVector &rowAbove, - const OutlineRowSolidRuns &solidRuns, - int y, OutlinePixels &result) + const OutlineRowSolidRuns &runs, + const bool *DVL_RESTRICT below, + bool *DVL_RESTRICT cur, + bool *DVL_RESTRICT above, + uint8_t y, + OutlinePixels &result) { - for (auto [xBegin, xEnd] : solidRuns) { - result.emplace_back(static_cast(xBegin - 1), static_cast(y)); - for (int xi = xBegin; xi < xEnd; ++xi) { - if (!rowAbove.test(xi)) { - result.emplace_back(static_cast(xi), static_cast(y - 1)); + DVL_ASSUME(!runs.empty()); + for (const auto &[begin, end] : runs) { + if (!cur[static_cast(begin - 1)]) { + result.emplace_back(static_cast(begin - 1), y); + cur[static_cast(begin - 1)] = true; + } + if (!cur[end]) { + result.emplace_back(end, y); + cur[end] = true; + } + for (uint8_t x = begin; x < end; ++x) { + if (!below[x]) { + result.emplace_back(x, static_cast(y + 1)); } - if (!rowBelow.test(xi)) { - result.emplace_back(static_cast(xi), static_cast(y + 1)); + if (!above[x]) { + result.emplace_back(x, static_cast(y - 1)); + above[x] = true; } } - result.emplace_back(static_cast(xEnd), static_cast(y)); } } -void AppendOutlineRowSolidRuns(int x, int w, OutlineRowSolidRuns &solidRuns) +void AppendOutlineRowSolidRuns(uint8_t x, uint8_t w, OutlineRowSolidRuns &solidRuns) { if (solidRuns.empty() || solidRuns.back().second != x) { solidRuns.emplace_back(x, x + w); } else { - solidRuns.back().second = static_cast(x + w); + solidRuns.back().second = static_cast(x + w); } } @@ -300,17 +306,14 @@ void GetOutline(ClxSprite sprite, OutlinePixels &result) // NOLINT(readability-f { const unsigned width = sprite.width(); assert(width < MaxOutlineSpriteWidth); - StaticBitVector rows[3] = { - StaticBitVector(width), - StaticBitVector(width), - StaticBitVector(width) - }; - StaticBitVector *rowAbove = &rows[0]; - StaticBitVector *row = &rows[1]; - StaticBitVector *rowBelow = &rows[2]; - - int x = 0; - int y = sprite.height() - 1; + + int x = 1; + auto y = static_cast(sprite.height()); + + bool rows[3][MaxOutlineSpriteWidth + 2] = { {}, {}, {} }; + bool *rowAbove = rows[0]; + bool *row = rows[1]; + bool *rowBelow = rows[2]; OutlineRowSolidRuns solidRuns[2]; OutlineRowSolidRuns *solidRunAbove = &solidRuns[0]; @@ -319,7 +322,7 @@ void GetOutline(ClxSprite sprite, OutlinePixels &result) // NOLINT(readability-f const uint8_t *src = sprite.pixelData(); const uint8_t *const end = src + sprite.pixelDataSize(); while (src < end) { - while (x < static_cast(width)) { + while (x <= static_cast(width)) { const auto v = static_cast(*src++); uint8_t w; if (IsClxOpaque(v)) { @@ -363,36 +366,43 @@ void GetOutline(ClxSprite sprite, OutlinePixels &result) // NOLINT(readability-f } for (const auto &[xBegin, xEnd] : *solidRunAbove) { - rowAbove->set(xBegin, xEnd - xBegin); + std::fill(rowAbove + xBegin, rowAbove + xEnd, true); + } + + if (!solidRun->empty()) { + PopulateOutlinePixelsForRow(*solidRun, rowBelow, row, rowAbove, static_cast(y + 1), result); } - PopulateOutlinePixelsForRow(*rowBelow, *rowAbove, *solidRun, y + 1, result); // (0, 1, 2) => (2, 0, 1) std::swap(row, rowBelow); std::swap(row, rowAbove); - rowAbove->reset(); + std::fill_n(rowAbove, width, false); std::swap(solidRunAbove, solidRun); solidRunAbove->clear(); - if (x > static_cast(width)) { + if (x > static_cast(width + 1)) { // Transparent overrun. - const unsigned numWholeTransparentLines = x / width; + const unsigned numWholeTransparentLines = (x - 1) / width; if (numWholeTransparentLines > 1) { - PopulateOutlinePixelsForRow(*rowBelow, *rowAbove, *solidRun, y, result); + if (!solidRun->empty()) { + PopulateOutlinePixelsForRow(*solidRun, rowBelow, row, rowAbove, y, result); + } solidRun->clear(); - row->reset(); + std::fill_n(row, width, false); } - if (numWholeTransparentLines > 2) rowBelow->reset(); - y -= static_cast(numWholeTransparentLines); - x = static_cast(x % width); + if (numWholeTransparentLines > 2) std::fill_n(rowBelow, width, false); + y -= static_cast(numWholeTransparentLines); + x = static_cast((x - 1) % width) + 1; } else { --y; - x = 0; + x = 1; } } - rowAbove->reset(); - PopulateOutlinePixelsForRow(*rowBelow, *rowAbove, *solidRun, y + 1, result); + std::fill_n(rowAbove, width, false); + if (!solidRun->empty()) { + PopulateOutlinePixelsForRow(*solidRun, rowBelow, row, rowAbove, static_cast(y + 1), result); + } } template @@ -412,9 +422,10 @@ template void RenderClxOutline(const Surface &out, Point position, ClxSprite sprite, uint8_t color) { UpdateOutlinePixelsCache(sprite); - position.y -= sprite.height() - 1; - if (position.x > 0 && position.x + sprite.width() < out.w() - && position.y > 0 && position.y + sprite.height() < out.h()) { + --position.x; + position.y -= sprite.height(); + if (position.x >= 0 && position.x + sprite.width() < out.w() + && position.y >= 0 && position.y + sprite.height() < out.h()) { for (const auto &[x, y] : OutlinePixelsCache.outlinePixels) { *out.at(position.x + x, position.y + y) = color; } diff --git a/Source/utils/algorithm/container.hpp b/Source/utils/algorithm/container.hpp index 9d9212e98..6e49b28ad 100644 --- a/Source/utils/algorithm/container.hpp +++ b/Source/utils/algorithm/container.hpp @@ -115,4 +115,11 @@ container_internal::Iterator c_lower_bound(C &c, T &&value) std::forward(value)); } +template +container_internal::Iterator c_unique(C &c) +{ + return std::unique(container_internal::c_begin(c), + container_internal::c_end(c)); +} + } // namespace devilution diff --git a/Source/utils/static_vector.hpp b/Source/utils/static_vector.hpp index 92c7ebccb..13a0d0ed6 100644 --- a/Source/utils/static_vector.hpp +++ b/Source/utils/static_vector.hpp @@ -28,35 +28,18 @@ public: } } - [[nodiscard]] const T *begin() const - { - return &(*this)[0]; - } + [[nodiscard]] const T *begin() const { return &(*this)[0]; } + [[nodiscard]] T *begin() { return &(*this)[0]; } - [[nodiscard]] const T *end() const - { - return begin() + size_; - } + [[nodiscard]] const T *end() const { return begin() + size_; } + [[nodiscard]] T *end() { return begin() + size_; } - [[nodiscard]] size_t size() const - { - return size_; - } + [[nodiscard]] size_t size() const { return size_; } - [[nodiscard]] bool empty() const - { - return size_ == 0; - } + [[nodiscard]] bool empty() const { return size_ == 0; } - [[nodiscard]] T &back() - { - return (*this)[size_ - 1]; - } - - [[nodiscard]] const T &back() const - { - return (*this)[size_ - 1]; - } + [[nodiscard]] const T &back() const { return (*this)[size_ - 1]; } + [[nodiscard]] T &back() { return (*this)[size_ - 1]; } template T &emplace_back(Args &&...args) // NOLINT(readability-identifier-naming) @@ -65,22 +48,20 @@ public: return *::new (&data_[size_++]) T(std::forward(args)...); } - T &operator[](std::size_t pos) - { - return *data_[pos].ptr(); - } + const T &operator[](std::size_t pos) const { return *data_[pos].ptr(); } + T &operator[](std::size_t pos) { return *data_[pos].ptr(); } - const T &operator[](std::size_t pos) const + void erase(const T *begin, const T *end) { - return *data_[pos].ptr(); + for (const T *it = begin; it < end; ++it) { + std::destroy_at(it); + } + size_ -= end - begin; } void clear() { - for (std::size_t pos = 0; pos < size_; ++pos) { - std::destroy_at(data_[pos].ptr()); - } - size_ = 0; + erase(begin(), end()); } ~StaticVector()