diff --git a/src/newgrf_airporttiles.cpp b/src/newgrf_airporttiles.cpp index eb67c83e20..f5b37a1706 100644 --- a/src/newgrf_airporttiles.cpp +++ b/src/newgrf_airporttiles.cpp @@ -243,7 +243,8 @@ uint16_t GetAirportTileCallback(CallbackID callback, uint32_t param1, uint32_t p static void AirportDrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *group, Colours colour) { - auto dts = group->ProcessRegisters(nullptr); + auto processor = group->ProcessRegisters(nullptr); + auto dts = processor.GetLayout(); SpriteID image = dts.ground.sprite; SpriteID pal = dts.ground.pal; diff --git a/src/newgrf_commons.cpp b/src/newgrf_commons.cpp index 6ad066bf64..e90c7b2843 100644 --- a/src/newgrf_commons.cpp +++ b/src/newgrf_commons.cpp @@ -562,10 +562,6 @@ bool Convert8bitBooleanCallback(const GRFFile *grffile, uint16_t cbid, uint16_t return cb_res != 0; } - -/* static */ std::vector NewGRFSpriteLayout::result_seq; - - /** * Allocate a spritelayout for \a num_sprites building sprites. * @param num_sprites Number of building sprites to allocate memory for. (not counting the terminator) @@ -590,41 +586,38 @@ void NewGRFSpriteLayout::AllocateRegisters() /** * Prepares a sprite layout before resolving action-1-2-3 chains. * Integrates offsets into the layout and determines which chains to resolve. - * @note The function uses statically allocated temporary storage, which is reused every time when calling the function. - * That means, you have to use the sprite layout before calling #PrepareLayout() the next time. + * @param raw_layout Sprite layout in need of preprocessing. * @param orig_offset Offset to apply to non-action-1 sprites. * @param newgrf_ground_offset Offset to apply to action-1 ground sprites. * @param newgrf_offset Offset to apply to action-1 non-ground sprites. * @param constr_stage Construction stage (0-3) to apply to all action-1 sprites. * @param separate_ground Whether the ground sprite shall be resolved by a separate action-1-2-3 chain by default. - * @return Bitmask of values for variable 10 to resolve action-1-2-3 chains for. */ -uint32_t NewGRFSpriteLayout::PrepareLayout(uint32_t orig_offset, uint32_t newgrf_ground_offset, uint32_t newgrf_offset, uint constr_stage, bool separate_ground) const +SpriteLayoutProcessor::SpriteLayoutProcessor(const NewGRFSpriteLayout &raw_layout, uint32_t orig_offset, uint32_t newgrf_ground_offset, uint32_t newgrf_offset, uint constr_stage, bool separate_ground) : + raw_layout(&raw_layout), separate_ground(separate_ground) { - result_seq.clear(); - uint32_t var10_values = 0; + this->result_seq.reserve(this->raw_layout->seq.size() + 1); /* Create a copy of the spritelayout, so we can modify some values. * Also include the groundsprite into the sequence for easier processing. */ - DrawTileSeqStruct © = result_seq.emplace_back(); - copy.image = ground; + DrawTileSeqStruct © = this->result_seq.emplace_back(); + copy.image = this->raw_layout->ground; copy.delta_z = static_cast(0x80); - for (const DrawTileSeqStruct &dtss : this->seq) { - result_seq.emplace_back(dtss); - } + this->result_seq.insert(this->result_seq.end(), this->raw_layout->seq.begin(), this->raw_layout->seq.end()); + /* Determine the var10 values the action-1-2-3 chains needs to be resolved for, * and apply the default sprite offsets (unless disabled). */ - const TileLayoutRegisters *regs = this->registers.empty() ? nullptr : this->registers.data(); + const TileLayoutRegisters *regs = this->raw_layout->registers.empty() ? nullptr : this->raw_layout->registers.data(); bool ground = true; - for (DrawTileSeqStruct &result : result_seq) { + for (DrawTileSeqStruct &result : this->result_seq) { TileLayoutFlags flags = TLF_NOTHING; if (regs != nullptr) flags = regs->flags; /* Record var10 value for the sprite */ if (HasBit(result.image.sprite, SPRITE_MODIFIER_CUSTOM_SPRITE) || (flags & TLF_SPRITE_REG_FLAGS)) { - uint8_t var10 = (flags & TLF_SPRITE_VAR10) ? regs->sprite_var10 : (ground && separate_ground ? 1 : 0); - SetBit(var10_values, var10); + uint8_t var10 = (flags & TLF_SPRITE_VAR10) ? regs->sprite_var10 : (ground && this->separate_ground ? 1 : 0); + SetBit(this->var10_values, var10); } /* Add default sprite offset, unless there is a custom one */ @@ -639,8 +632,8 @@ uint32_t NewGRFSpriteLayout::PrepareLayout(uint32_t orig_offset, uint32_t newgrf /* Record var10 value for the palette */ if (HasBit(result.image.pal, SPRITE_MODIFIER_CUSTOM_SPRITE) || (flags & TLF_PALETTE_REG_FLAGS)) { - uint8_t var10 = (flags & TLF_PALETTE_VAR10) ? regs->palette_var10 : (ground && separate_ground ? 1 : 0); - SetBit(var10_values, var10); + uint8_t var10 = (flags & TLF_PALETTE_VAR10) ? regs->palette_var10 : (ground && this->separate_ground ? 1 : 0); + SetBit(this->var10_values, var10); } /* Add default palette offset, unless there is a custom one */ @@ -654,30 +647,26 @@ uint32_t NewGRFSpriteLayout::PrepareLayout(uint32_t orig_offset, uint32_t newgrf ground = false; if (regs != nullptr) regs++; } - - return var10_values; } /** * Evaluates the register modifiers and integrates them into the preprocessed sprite layout. - * @pre #PrepareLayout() needs calling first. * @param resolved_var10 The value of var10 the action-1-2-3 chain was evaluated for. * @param resolved_sprite Result sprite of the action-1-2-3 chain. - * @param separate_ground Whether the ground sprite is resolved by a separate action-1-2-3 chain. - * @return Resulting spritelayout after processing the registers. */ -void NewGRFSpriteLayout::ProcessRegisters(uint8_t resolved_var10, uint32_t resolved_sprite, bool separate_ground) const +void SpriteLayoutProcessor::ProcessRegisters(uint8_t resolved_var10, uint32_t resolved_sprite) { - const TileLayoutRegisters *regs = this->registers.empty() ? nullptr : this->registers.data(); + assert(this->raw_layout != nullptr); + const TileLayoutRegisters *regs = this->raw_layout->registers.empty() ? nullptr : this->raw_layout->registers.data(); bool ground = true; - for (DrawTileSeqStruct &result : result_seq) { + for (DrawTileSeqStruct &result : this->result_seq) { TileLayoutFlags flags = TLF_NOTHING; if (regs != nullptr) flags = regs->flags; /* Is the sprite or bounding box affected by an action-1-2-3 chain? */ if (HasBit(result.image.sprite, SPRITE_MODIFIER_CUSTOM_SPRITE) || (flags & TLF_SPRITE_REG_FLAGS)) { /* Does the var10 value apply to this sprite? */ - uint8_t var10 = (flags & TLF_SPRITE_VAR10) ? regs->sprite_var10 : (ground && separate_ground ? 1 : 0); + uint8_t var10 = (flags & TLF_SPRITE_VAR10) ? regs->sprite_var10 : (ground && this->separate_ground ? 1 : 0); if (var10 == resolved_var10) { /* Apply registers */ if ((flags & TLF_DODRAW) && GetRegister(regs->dodraw) == 0) { @@ -710,7 +699,7 @@ void NewGRFSpriteLayout::ProcessRegisters(uint8_t resolved_var10, uint32_t resol /* Is the palette affected by an action-1-2-3 chain? */ if (result.image.sprite != 0 && (HasBit(result.image.pal, SPRITE_MODIFIER_CUSTOM_SPRITE) || (flags & TLF_PALETTE_REG_FLAGS))) { /* Does the var10 value apply to this sprite? */ - uint8_t var10 = (flags & TLF_PALETTE_VAR10) ? regs->palette_var10 : (ground && separate_ground ? 1 : 0); + uint8_t var10 = (flags & TLF_PALETTE_VAR10) ? regs->palette_var10 : (ground && this->separate_ground ? 1 : 0); if (var10 == resolved_var10) { /* Apply registers */ if (HasBit(result.image.pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) result.image.pal += resolved_sprite; diff --git a/src/newgrf_commons.h b/src/newgrf_commons.h index a0d639514b..0b9b10fdae 100644 --- a/src/newgrf_commons.h +++ b/src/newgrf_commons.h @@ -19,6 +19,7 @@ #include "direction_type.h" #include "company_type.h" #include "cargo_type.h" +#include "core/bitmath_func.hpp" /** Context for tile accesses */ enum TileContext : uint8_t { @@ -124,9 +125,8 @@ struct NewGRFSpriteLayout : DrawTileSprites { void AllocateRegisters(); /** - * Tests whether this spritelayout needs preprocessing by - * #PrepareLayout() and #ProcessRegisters(), or whether it can be - * used directly. + * Tests whether this spritelayout needs preprocessing by SpriteLayoutProcessor, + * or whether it can be used directly. * @return true if preprocessing is needed */ bool NeedsPreprocessing() const @@ -134,24 +134,48 @@ struct NewGRFSpriteLayout : DrawTileSprites { return !this->registers.empty(); } - uint32_t PrepareLayout(uint32_t orig_offset, uint32_t newgrf_ground_offset, uint32_t newgrf_offset, uint constr_stage, bool separate_ground) const; - void ProcessRegisters(uint8_t resolved_var10, uint32_t resolved_sprite, bool separate_ground) const; + std::span GetSequence() const override { return {this->seq.begin(), this->seq.end()}; } +}; + +/** + * Add dynamic register values to a sprite layout. + */ +class SpriteLayoutProcessor { + const NewGRFSpriteLayout *raw_layout = nullptr; + std::vector result_seq; + uint32_t var10_values = 0; + bool separate_ground = false; +public: + SpriteLayoutProcessor() = default; + + /** Constructor for spritelayout, which do not need preprocessing. */ + SpriteLayoutProcessor(const NewGRFSpriteLayout &raw_layout) : raw_layout(&raw_layout) {} + + SpriteLayoutProcessor(const NewGRFSpriteLayout &raw_layout, uint32_t orig_offset, uint32_t newgrf_ground_offset, uint32_t newgrf_offset, uint constr_stage, bool separate_ground); + + /** + * Get values for variable 10 to resolve sprites for. + * NewStations only. + */ + SetBitIterator Var10Values() const { return this->var10_values; } + + void ProcessRegisters(uint8_t resolved_var10, uint32_t resolved_sprite); /** * Returns the result spritelayout after preprocessing. - * @pre #PrepareLayout() and #ProcessRegisters() need calling first. - * @return result spritelayout + * @return result ground sprite and spritelayout */ DrawTileSpriteSpan GetLayout() const { - return {result_seq[0].image, {++result_seq.begin(), result_seq.end()}}; + assert(this->raw_layout != nullptr); + if (this->result_seq.empty()) { + /* Simple layout without preprocessing. */ + return {this->raw_layout->ground, this->raw_layout->seq}; + } else { + /* Dynamic layout with preprocessing. */ + return {this->result_seq[0].image, {++this->result_seq.begin(), this->result_seq.end()}}; + } } - - std::span GetSequence() const override { return {this->seq.begin(), this->seq.end()}; } - - -private: - static std::vector result_seq; ///< Temporary storage when preprocessing spritelayouts. }; /** diff --git a/src/newgrf_house.cpp b/src/newgrf_house.cpp index 7562cd4df2..d5feda3f7d 100644 --- a/src/newgrf_house.cpp +++ b/src/newgrf_house.cpp @@ -463,7 +463,8 @@ uint16_t GetHouseCallback(CallbackID callback, uint32_t param1, uint32_t param2, static void DrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *group, uint8_t stage, HouseID house_id) { - auto dts = group->ProcessRegisters(&stage); + auto processor = group->ProcessRegisters(&stage); + auto dts = processor.GetLayout(); const HouseSpec *hs = HouseSpec::Get(house_id); PaletteID palette = GetColourPalette(hs->random_colour[TileHash2Bit(ti->x, ti->y)]); @@ -528,7 +529,8 @@ void DrawNewHouseTileInGUI(int x, int y, const HouseSpec *spec, HouseID house_id if (group == nullptr) return; uint8_t stage = TOWN_HOUSE_COMPLETED; - auto dts = group->ProcessRegisters(&stage); + auto processor = group->ProcessRegisters(&stage); + auto dts = processor.GetLayout(); PaletteID palette = GetColourPalette(spec->random_colour[0]); if (spec->callback_mask.Test(HouseCallbackMask::Colour)) { diff --git a/src/newgrf_industrytiles.cpp b/src/newgrf_industrytiles.cpp index e39b330e46..0976178fdd 100644 --- a/src/newgrf_industrytiles.cpp +++ b/src/newgrf_industrytiles.cpp @@ -160,7 +160,8 @@ uint32_t IndustryTileResolverObject::GetDebugID() const static void IndustryDrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *group, Colours rnd_colour, uint8_t stage) { - auto dts = group->ProcessRegisters(&stage); + auto processor = group->ProcessRegisters(&stage); + auto dts = processor.GetLayout(); SpriteID image = dts.ground.sprite; PaletteID pal = dts.ground.pal; diff --git a/src/newgrf_object.cpp b/src/newgrf_object.cpp index 108df39e23..36d46b7a64 100644 --- a/src/newgrf_object.cpp +++ b/src/newgrf_object.cpp @@ -444,7 +444,8 @@ uint16_t GetObjectCallback(CallbackID callback, uint32_t param1, uint32_t param2 */ static void DrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *group, const ObjectSpec *spec) { - auto dts = group->ProcessRegisters(nullptr); + auto processor = group->ProcessRegisters(nullptr); + auto dts = processor.GetLayout(); PaletteID palette = (spec->flags.Test(ObjectFlag::Uses2CC) ? SPR_2CCMAP_BASE : PALETTE_RECOLOUR_START) + Object::GetByTile(ti->tile)->colour; SpriteID image = dts.ground.sprite; @@ -492,7 +493,8 @@ void DrawNewObjectTileInGUI(int x, int y, const ObjectSpec *spec, uint8_t view) const auto *group = object.Resolve(); if (group == nullptr) return; - auto dts = group->ProcessRegisters(nullptr); + auto processor = group->ProcessRegisters(nullptr); + auto dts = processor.GetLayout(); PaletteID palette; if (Company::IsValidID(_local_company)) { diff --git a/src/newgrf_roadstop.cpp b/src/newgrf_roadstop.cpp index f17399400b..82c3d30c6a 100644 --- a/src/newgrf_roadstop.cpp +++ b/src/newgrf_roadstop.cpp @@ -293,7 +293,8 @@ void DrawRoadStopTile(int x, int y, RoadType roadtype, const RoadStopSpec *spec, RoadStopResolverObject object(spec, nullptr, INVALID_TILE, roadtype, type, view); const auto *group = object.Resolve(); if (group == nullptr) return; - auto dts = group->ProcessRegisters(nullptr); + auto processor = group->ProcessRegisters(nullptr); + auto dts = processor.GetLayout(); PaletteID palette = GetCompanyPalette(_local_company); diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp index 0ccff23851..63347e84c8 100644 --- a/src/newgrf_spritegroup.cpp +++ b/src/newgrf_spritegroup.cpp @@ -287,19 +287,19 @@ static bool RangeHighComparator(const DeterministicSpriteGroupRange &range, uint * @param[in,out] stage Construction stage (0-3), or nullptr if not applicable. * @return sprite layout to draw. */ -DrawTileSpriteSpan TileLayoutSpriteGroup::ProcessRegisters(uint8_t *stage) const +SpriteLayoutProcessor TileLayoutSpriteGroup::ProcessRegisters(uint8_t *stage) const { if (!this->dts.NeedsPreprocessing()) { if (stage != nullptr && this->dts.consistent_max_offset > 0) *stage = GetConstructionStageOffset(*stage, this->dts.consistent_max_offset); - return {this->dts.ground, this->dts.seq}; + return SpriteLayoutProcessor(this->dts); } uint8_t actual_stage = stage != nullptr ? *stage : 0; - this->dts.PrepareLayout(0, 0, 0, actual_stage, false); - this->dts.ProcessRegisters(0, 0, false); + SpriteLayoutProcessor result(this->dts, 0, 0, 0, actual_stage, false); + result.ProcessRegisters(0, 0); /* Stage has been processed by PrepareLayout(), set it to zero. */ if (stage != nullptr) *stage = 0; - return this->dts.GetLayout(); + return result; } diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h index b117c3491f..b8121dcef9 100644 --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -260,7 +260,7 @@ struct TileLayoutSpriteGroup : SpriteGroup { NewGRFSpriteLayout dts{}; - DrawTileSpriteSpan ProcessRegisters(uint8_t *stage) const; + SpriteLayoutProcessor ProcessRegisters(uint8_t *stage) const; }; struct IndustryProductionSpriteGroup : SpriteGroup { diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index 1824d39e17..16dd6ee8a4 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -803,6 +803,7 @@ bool DrawStationTile(int x, int y, RailType railtype, Axis axis, StationClassID uint32_t relocation = 0; uint32_t ground_relocation = 0; const NewGRFSpriteLayout *layout = nullptr; + SpriteLayoutProcessor processor; // owns heap, borrowed by tmp_rail_layout and sprites DrawTileSpriteSpan tmp_rail_layout; if (statspec->renderdata.empty()) { @@ -818,13 +819,12 @@ bool DrawStationTile(int x, int y, RailType railtype, Axis axis, StationClassID if (layout != nullptr) { /* Sprite layout which needs preprocessing */ bool separate_ground = statspec->flags.Test(StationSpecFlag::SeparateGround); - uint32_t var10_values = layout->PrepareLayout(total_offset, rti->fallback_railtype, 0, 0, separate_ground); - for (uint8_t var10 : SetBitIterator(var10_values)) { + processor = SpriteLayoutProcessor(*layout, total_offset, rti->fallback_railtype, 0, 0, separate_ground); + for (uint8_t var10 : processor.Var10Values()) { uint32_t var10_relocation = GetCustomStationRelocation(statspec, nullptr, INVALID_TILE, var10); - layout->ProcessRegisters(var10, var10_relocation, separate_ground); + processor.ProcessRegisters(var10, var10_relocation); } - - tmp_rail_layout = layout->GetLayout(); + tmp_rail_layout = processor.GetLayout(); sprites = &tmp_rail_layout; total_offset = 0; } else { diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 0f93d36c80..fc4b24d227 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -3076,6 +3076,7 @@ bool SplitGroundSpriteForOverlay(const TileInfo *ti, SpriteID *ground, RailTrack static void DrawTile_Station(TileInfo *ti) { const NewGRFSpriteLayout *layout = nullptr; + SpriteLayoutProcessor processor; // owns heap, borrowed by tmp_layout and t DrawTileSpriteSpan tmp_layout; const DrawTileSprites *t = nullptr; int32_t total_offset; @@ -3268,12 +3269,12 @@ draw_default_foundation: if (layout != nullptr) { /* Sprite layout which needs preprocessing */ bool separate_ground = statspec->flags.Test(StationSpecFlag::SeparateGround); - uint32_t var10_values = layout->PrepareLayout(total_offset, rti->fallback_railtype, 0, 0, separate_ground); - for (uint8_t var10 : SetBitIterator(var10_values)) { + processor = SpriteLayoutProcessor(*layout, total_offset, rti->fallback_railtype, 0, 0, separate_ground); + for (uint8_t var10 : processor.Var10Values()) { uint32_t var10_relocation = GetCustomStationRelocation(statspec, st, ti->tile, var10); - layout->ProcessRegisters(var10, var10_relocation, separate_ground); + processor.ProcessRegisters(var10, var10_relocation); } - tmp_layout = layout->GetLayout(); + tmp_layout = processor.GetLayout(); t = &tmp_layout; total_offset = 0; } else if (statspec != nullptr) { @@ -3337,7 +3338,8 @@ draw_default_foundation: if (type == StationType::RoadWaypoint && stop_draw_mode.Test(RoadStopDrawMode::WaypGround)) { draw_ground = true; } - tmp_layout = group->ProcessRegisters(nullptr); + processor = group->ProcessRegisters(nullptr); + tmp_layout = processor.GetLayout(); t = &tmp_layout; } }