diff --git a/src/bitmap_type.h b/src/bitmap_type.h index 3281e141d8..bd722f346e 100644 --- a/src/bitmap_type.h +++ b/src/bitmap_type.h @@ -11,19 +11,38 @@ #define BITMAP_TYPE_HPP #include "tilearea_type.h" +#include "core/bitmath_func.hpp" +#include "core/geometry_type.hpp" +#include "core/math_func.hpp" +#include #include /** Represents a tile area containing containing individually set tiles. * Each tile must be contained within the preallocated area. - * A std::vector is used to mark which tiles are contained. + * A std::vector is used to mark which tiles are contained. */ class BitmapTileArea : public TileArea { + friend struct BitmapTileIterator; + protected: - std::vector data; + using BlockT = uint32_t; + static constexpr uint BLOCK_BITS = std::numeric_limits::digits; - inline uint Index(uint x, uint y) const { return y * this->w + x; } + std::vector data; - inline uint Index(TileIndex tile) const { return Index(TileX(tile) - TileX(this->tile), TileY(tile) - TileY(this->tile)); } + inline uint RowPitch() const { return CeilDivT(this->w, BLOCK_BITS); } + + inline void ResetData() + { + this->data.assign(this->h * this->RowPitch(), 0); + } + + inline std::pair Index(uint x, uint y) const + { + return { (y * this->RowPitch()) + (x / BLOCK_BITS), x % BLOCK_BITS }; + } + + inline std::pair Index(TileIndex tile) const { return this->Index(TileX(tile) - TileX(this->tile), TileY(tile) - TileY(this->tile)); } public: BitmapTileArea() @@ -38,7 +57,7 @@ public: this->tile = ta.tile; this->w = ta.w; this->h = ta.h; - this->data.resize(Index(this->w, this->h)); + this->ResetData(); } /** @@ -61,8 +80,7 @@ public: this->tile = TileXY(r.left, r.top); this->w = r.Width(); this->h = r.Height(); - this->data.clear(); - this->data.resize(Index(w, h)); + this->ResetData(); } void Initialize(const TileArea &ta) @@ -70,8 +88,7 @@ public: this->tile = ta.tile; this->w = ta.w; this->h = ta.h; - this->data.clear(); - this->data.resize(Index(w, h)); + this->ResetData(); } /** @@ -81,7 +98,8 @@ public: inline void SetTile(TileIndex tile) { assert(this->Contains(tile)); - this->data[Index(tile)] = true; + auto pos = this->Index(tile); + SetBit(this->data[pos.first], pos.second); } /** @@ -91,16 +109,21 @@ public: inline void ClrTile(TileIndex tile) { assert(this->Contains(tile)); - this->data[Index(tile)] = false; + auto pos = this->Index(tile); + ClrBit(this->data[pos.first], pos.second); } + void SetTiles(const TileArea &area); + /** * Test if a tile is part of the tile area. * @param tile Tile to check */ inline bool HasTile(TileIndex tile) const { - return this->Contains(tile) && this->data[Index(tile)]; + if (!this->Contains(tile)) return false; + auto pos = this->Index(tile); + return HasBit(this->data[pos.first], pos.second); } inline bool operator==(const BitmapTileArea &other) const @@ -110,25 +133,62 @@ public: }; /** Iterator to iterate over all tiles belonging to a bitmaptilearea. */ -class BitmapTileIterator : public OrthogonalTileIterator { +class BitmapTileIterator : public TileIterator { protected: - const BitmapTileArea *bitmap; + BitmapTileArea::BlockT block; ///< Current block data. + const BitmapTileArea::BlockT *next; ///< Next block pointer. + uint block_x; ///< The current 'x' position in the block iteration. + uint y; ///< The current 'y' position in the rectangle. + TileIndex row_start; ///< Row start tile. + uint pitch; ///< The row pitch. + + void advance() + { + while (this->block == 0) { + this->block_x++; + if (this->block_x == this->pitch) { + /* Next row */ + this->y--; + if (this->y == 0) { + /* End */ + this->tile = INVALID_TILE; + return; + } + this->block_x = 0; + this->row_start += MapSizeX(); + } + /* Read next block and advance next pointer */ + this->block = *this->next; + this->next++; + } + + uint8_t bit = FindFirstBit(this->block); + this->block = KillFirstBit(this->block); + this->tile = this->row_start + (this->block_x * BitmapTileArea::BLOCK_BITS) + bit; + return; + } + public: /** * Construct the iterator. * @param bitmap BitmapTileArea to iterate. */ - BitmapTileIterator(const BitmapTileArea &bitmap) : OrthogonalTileIterator(bitmap), bitmap(&bitmap) + BitmapTileIterator(const BitmapTileArea &bitmap) : TileIterator(), block_x(0), y(bitmap.h), row_start(bitmap.tile), pitch(bitmap.RowPitch()) { - if (!this->bitmap->HasTile(TileIndex(this->tile))) ++(*this); + if (!bitmap.data.empty()) { + this->block = bitmap.data[0]; + this->next = bitmap.data.data() + 1; + this->advance(); + } else { + this->block = 0; + this->next = nullptr; + } } inline TileIterator& operator ++() override { - (*this).OrthogonalTileIterator::operator++(); - while (this->tile != INVALID_TILE && !this->bitmap->HasTile(TileIndex(this->tile))) { - (*this).OrthogonalTileIterator::operator++(); - } + assert(this->tile != INVALID_TILE); + this->advance(); return *this; } diff --git a/src/station.cpp b/src/station.cpp index 9003d327cf..6e7b4e8536 100644 --- a/src/station.cpp +++ b/src/station.cpp @@ -544,7 +544,7 @@ void Station::RecomputeCatchment(bool no_clear_nearby_lists) /* This tile sub-loop doesn't need to test any tiles, they are simply added to the catchment set. */ TileArea ta2 = TileArea(tile, 1, 1).Expand(r); - for (TileIndex tile2 : ta2) this->catchment_tiles.SetTile(tile2); + this->catchment_tiles.SetTiles(ta2); } /* Search catchment tiles for towns and industries */ diff --git a/src/tilearea.cpp b/src/tilearea.cpp index a2689bb856..cbe58a833b 100644 --- a/src/tilearea.cpp +++ b/src/tilearea.cpp @@ -10,6 +10,7 @@ #include "stdafx.h" #include "tilearea_type.h" +#include "bitmap_type.h" #include "safeguards.h" @@ -295,3 +296,43 @@ TileIterator &DiagonalTileIterator::operator++() } return std::make_unique(corner1, corner2); } + +/** + * Add a set of tiles as part of the tile area. + * @param area Tile area to add. + */ +void BitmapTileArea::SetTiles(const TileArea &area) +{ + if (area.w == 0 || area.h == 0) return; + + assert(this->Contains(area.tile)); + assert(this->Contains(area.tile + TileDiffXY(area.w - 1, area.h - 1))); + + auto [row_idx, start_bit] = this->Index(area.tile); + if (start_bit + area.w <= BLOCK_BITS) { + /* Update only one block */ + BlockT mask = GetBitMaskSC(start_bit, area.w); + for (uint h = area.h; h > 0; h--, row_idx += this->RowPitch()) { + this->data[row_idx] |= mask; + } + } else { + /* Update multiple blocks */ + for (uint h = area.h; h > 0; h--, row_idx += this->RowPitch()) { + uint idx = row_idx; + this->data[idx] |= (~static_cast(0)) << start_bit; + uint w = area.w - (BLOCK_BITS - start_bit); + while (w > 0) { + idx++; + if (w < BLOCK_BITS) { + /* Set LSBs, and finish row */ + this->data[idx] |= GetBitMaskSC(0, w); + break; + } else { + /* Set all bits */ + this->data[idx] = ~static_cast(0); + w -= BLOCK_BITS; + } + } + } + } +}