From e835673c5e8499bd67f0f5ac0e52d9c77f5bfb88 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 20 Jun 2025 12:57:28 +0200 Subject: [PATCH] Extract bitmap class --- src/client/imagefilters.cpp | 46 +----------------- src/util/bitmap.h | 93 +++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 44 deletions(-) create mode 100644 src/util/bitmap.h diff --git a/src/client/imagefilters.cpp b/src/client/imagefilters.cpp index 8b38e9ad9..c51743765 100644 --- a/src/client/imagefilters.cpp +++ b/src/client/imagefilters.cpp @@ -4,54 +4,12 @@ #include "imagefilters.h" #include "util/numeric.h" +#include "util/bitmap.h" #include #include -#include #include #include -// Simple 2D bitmap class with just the functionality needed here -class Bitmap { - u32 linesize, lines; - std::vector data; - - static inline u32 bytepos(u32 index) { return index >> 3; } - static inline u8 bitpos(u32 index) { return index & 7; } - -public: - Bitmap(u32 width, u32 height) : linesize(width), lines(height), - data(bytepos(width * height) + 1) {} - - inline bool get(u32 x, u32 y) const { - u32 index = y * linesize + x; - return data[bytepos(index)] & (1 << bitpos(index)); - } - - inline void set(u32 x, u32 y) { - u32 index = y * linesize + x; - data[bytepos(index)] |= 1 << bitpos(index); - } - - inline bool all() const { - for (u32 i = 0; i < data.size() - 1; i++) { - if (data[i] != 0xff) - return false; - } - // last byte not entirely filled - for (u8 i = 0; i < bitpos(linesize * lines); i++) { - bool value_of_bit = data.back() & (1 << i); - if (!value_of_bit) - return false; - } - return true; - } - - inline void copy(Bitmap &to) const { - assert(to.linesize == linesize && to.lines == lines); - to.data = data; - } -}; - template static void imageCleanTransparentWithInlining(video::IImage *src, u32 threshold) { @@ -143,7 +101,7 @@ static void imageCleanTransparentWithInlining(video::IImage *src, u32 threshold) // Apply changes to bitmap for next run. This is done so we don't introduce // a bias in color propagation in the direction pixels are processed. - newmap.copy(bitmap); + bitmap = newmap; } } diff --git a/src/util/bitmap.h b/src/util/bitmap.h new file mode 100644 index 000000000..39e114c8e --- /dev/null +++ b/src/util/bitmap.h @@ -0,0 +1,93 @@ +// Luanti +// SPDX-License-Identifier: LGPL-2.1-or-later +// Copyright (C) 2021-2025 sfan5 + +#include "irrlichttypes.h" +#include +#include +#include + +/** + * Rudimentary header-only 2D bitmap class. + * @warning not thread-safe + */ +class Bitmap { + u32 linesize, lines; + std::vector data; + + static inline u32 bytepos(u32 index) { return index >> 3; } + static inline u8 bitpos(u32 index) { return index & 7; } + + template + bool modify_(u32 x, u32 y) + { + u32 index = y * linesize + x; + u8 mask = 1 << bitpos(index); + u8 byte = data[bytepos(index)]; + if constexpr (set) + byte |= mask; + else if constexpr (toggle) + byte ^= mask; + else if constexpr (clear) + byte &= ~mask; + data[bytepos(index)] = byte; + return byte & mask; + } + +public: + /// @brief Create an empty bitmap + Bitmap() : linesize(0), lines(0) {} + + /// @brief Create a new zero-filled bitmap + Bitmap(u32 width, u32 height) + { + resize(width, height); + } + + inline u32 width() const { return linesize; } + inline u32 height() const { return lines; } + + inline void resize(u32 width, u32 height, bool initial_value=false) + { + assert(width <= 65534 && height <= 65534); // index would overflow + linesize = width; + lines = height; + data.clear(); // make sure to discard all data + if (width && height) + data.resize(bytepos(width * height) + 1, static_cast(initial_value ? 0xff : 0)); + } + + inline void reset(bool value) + { + std::fill(data.begin(), data.end(), value ? 0xff : 0); + } + + inline bool get(u32 x, u32 y) const + { + u32 index = y * linesize + x; + return data[bytepos(index)] & (1 << bitpos(index)); + } + + inline void set(u32 x, u32 y) { modify_<1, 0, 0>(x, y); } + inline void unset(u32 x, u32 y) { modify_<0, 0, 1>(x, y); } + inline bool toggle(u32 x, u32 y) { return modify_<0, 1, 0>(x, y); } + + /// @brief Returns true if all bits in the bitmap are set + inline bool all() const + { + if (!linesize || !lines) + return (assert(0), true); + for (u32 i = 0; i < data.size() - 1; i++) { + if (data[i] != 0xff) + return false; + } + u8 last_byte = data.back(); // not used entirely + for (u8 i = 0; i < bitpos(linesize * lines); i++) { + if (!(last_byte & (1 << i))) + return false; + } + return true; + } +}; + +// Note: a 3D class could be written based on the 2D one. Or maybe the other way?