15 int width,
int height,
int tile_size,
int num_tiles,
35 const std::vector<uint8_t>& data) {
65 if (tile_data.empty()) {
87 if (tiles_per_row <= 0) {
91 int tile_x = (tile_id % tiles_per_row) * tilemap.
tile_size.
x;
92 int tile_y = (tile_id / tiles_per_row) * tilemap.
tile_size.
y;
95 if (tile_x < 0 || tile_x >= tilemap.
atlas.
width() || tile_y < 0 ||
104 if (tile_data.empty()) {
121 int tile_x = (tile_id % tiles_per_row) * tilemap.
tile_size.
x;
122 int tile_y = (tile_id / tiles_per_row) * tilemap.
tile_size.
y;
125 int tile_data_offset = 0;
141 const std::vector<uint8_t>& data,
int tile_id,
int sheet_offset) {
142 const int tile_width = 8;
143 const int tile_height = 8;
144 const int buffer_width = 128;
145 const int sheet_height = 32;
147 const int tiles_per_row = buffer_width / tile_width;
148 const int rows_per_sheet = sheet_height / tile_height;
149 const int tiles_per_sheet = tiles_per_row * rows_per_sheet;
151 int sheet = (tile_id / tiles_per_sheet) % 4 + sheet_offset;
152 int position_in_sheet = tile_id % tiles_per_sheet;
153 int row_in_sheet = position_in_sheet / tiles_per_row;
154 int column_in_sheet = position_in_sheet % tiles_per_row;
157 if (sheet < sheet_offset || sheet > sheet_offset + 3) {
158 return std::vector<uint8_t>(tile_width * tile_height, 0);
161 const int data_size =
static_cast<int>(data.size());
162 std::vector<uint8_t> tile_data(tile_width * tile_height, 0);
163 for (
int y = 0; y < tile_height; ++y) {
164 for (
int x = 0; x < tile_width; ++x) {
165 int src_x = column_in_sheet * tile_width + x;
166 int src_y = (sheet * sheet_height) + (row_in_sheet * tile_height) + y;
168 int src_index = (src_y * buffer_width) + src_x;
169 int dest_index = y * tile_width + x;
172 if (src_index >= 0 && src_index < data_size) {
173 tile_data[dest_index] = data[src_index];
183 for (
int y = 0; y < 4; ++y) {
184 for (
int x = 0; x < 8; ++x) {
185 std::swap(tile_data[y * 8 + x], tile_data[(7 - y) * 8 + x]);
191 for (
int y = 0; y < 8; ++y) {
192 for (
int x = 0; x < 4; ++x) {
193 std::swap(tile_data[y * 8 + x], tile_data[y * 8 + (7 - x)]);
199 const TileInfo& tile_info,
int base_x,
int base_y,
201 std::vector<uint8_t> tile_data =
211 for (
int y = 0; y < 8; ++y) {
212 for (
int x = 0; x < 8; ++x) {
213 int src_index = y * 8 + x;
214 int dest_x = base_x + x;
215 int dest_y = base_y + y;
216 int dest_index = (dest_y * tilemap.
atlas.
width()) + dest_x;
226 int sheet_offset,
int tile_id) {
229 int tile16_row = tile_id / tiles_per_row;
230 int tile16_column = tile_id % tiles_per_row;
231 int base_x = tile16_column * tilemap.
tile_size.
x;
232 int base_y = tile16_row * tilemap.
tile_size.
y;
235 ComposeAndPlaceTilePart(tilemap, data, top_left, base_x, base_y,
237 ComposeAndPlaceTilePart(tilemap, data, top_right, base_x + 8, base_y,
239 ComposeAndPlaceTilePart(tilemap, data, bottom_left, base_x, base_y + 8,
241 ComposeAndPlaceTilePart(tilemap, data, bottom_right, base_x + 8, base_y + 8,
244 tilemap.
tile_info[tile_id] = {top_left, top_right, bottom_left, bottom_right};
251 int num_tiles = tilemap.
tile_info.size();
253 int tile16_row = num_tiles / tiles_per_row;
254 int tile16_column = num_tiles % tiles_per_row;
255 int base_x = tile16_column * tilemap.
tile_size.
x;
256 int base_y = tile16_row * tilemap.
tile_size.
y;
258 ComposeAndPlaceTilePart(tilemap, data, top_left, base_x, base_y,
260 ComposeAndPlaceTilePart(tilemap, data, top_right, base_x + 8, base_y,
262 ComposeAndPlaceTilePart(tilemap, data, bottom_left, base_x, base_y + 8,
264 ComposeAndPlaceTilePart(tilemap, data, bottom_right, base_x + 8, base_y + 8,
267 tilemap.
tile_info.push_back({top_left, top_right, bottom_left, bottom_right});
273 SDL_Log(
"GetTilemapData: Invalid tile_id %d (negative)", tile_id);
274 return std::vector<uint8_t>(256, 0);
278 SDL_Log(
"GetTilemapData: Atlas is not active for tile_id %d", tile_id);
279 return std::vector<uint8_t>(256, 0);
283 SDL_Log(
"GetTilemapData: Atlas vector is empty for tile_id %d", tile_id);
284 return std::vector<uint8_t>(256, 0);
288 SDL_Log(
"GetTilemapData: Invalid tile size (%d, %d) for tile_id %d",
290 return std::vector<uint8_t>(256, 0);
298 if (width <= 0 || height <= 0) {
299 SDL_Log(
"GetTilemapData: Invalid atlas dimensions (%d, %d) for tile_id %d",
300 width, height, tile_id);
301 return std::vector<uint8_t>(tile_size * tile_size, 0);
305 int tiles_per_row = width / tile_size;
306 int tiles_per_column = height / tile_size;
307 int max_tile_id = tiles_per_row * tiles_per_column - 1;
309 if (tile_id > max_tile_id) {
311 "GetTilemapData: tile_id %d exceeds maximum %d (atlas: %dx%d, "
313 tile_id, max_tile_id, width, height, tile_size);
314 return std::vector<uint8_t>(tile_size * tile_size, 0);
317 std::vector<uint8_t> data(tile_size * tile_size);
319 for (
int ty = 0; ty < tile_size; ty++) {
320 for (
int tx = 0; tx < tile_size; tx++) {
322 int tile_row = tile_id / tiles_per_row;
323 int tile_col = tile_id % tiles_per_row;
324 int atlas_x = tile_col * tile_size + tx;
325 int atlas_y = tile_row * tile_size + ty;
326 int atlas_index = atlas_y * width + atlas_x;
329 if (atlas_x >= 0 && atlas_x < width && atlas_y >= 0 && atlas_y < height &&
331 atlas_index <
static_cast<int>(tilemap.
atlas.
vector().size())) {
332 uint8_t value = tilemap.
atlas.
vector()[atlas_index];
333 data[ty * tile_size + tx] = value;
336 "GetTilemapData: Atlas position (%d, %d) or index %d out of bounds "
337 "(atlas: %dx%d, size: %zu)",
338 atlas_x, atlas_y, atlas_index, width, height,
340 data[ty * tile_size + tx] = 0;
349 const std::vector<int>& tile_ids,
350 const std::vector<std::pair<float, float>>& positions,
351 const std::vector<std::pair<float, float>>& scales) {
352 if (tile_ids.empty() || positions.empty() ||
353 tile_ids.size() != positions.size()) {
368 std::vector<RenderCommand> render_commands;
369 render_commands.reserve(tile_ids.size());
371 for (
size_t i = 0; i < tile_ids.size(); ++i) {
372 int tile_id = tile_ids[i];
373 float x = positions[i].first;
374 float y = positions[i].second;
377 float scale_x = 1.0F;
378 float scale_y = 1.0F;
379 if (i < scales.size()) {
380 scale_x = scales[i].first;
381 scale_y = scales[i].second;
398 if (cached_tile && cached_tile->
is_active()) {
406 int atlas_id = atlas_renderer.AddBitmap(*cached_tile);
408 render_commands.emplace_back(atlas_id, x, y, scale_x, scale_y);
414 if (!render_commands.empty()) {
415 atlas_renderer.RenderBatch(render_commands);
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
static AtlasRenderer & Get()
Represents a bitmap image optimized for SNES ROM hacking.
const SnesPalette & palette() const
void WriteToPixel(int position, uint8_t value)
Write a value to a pixel at the given position.
TextureHandle texture() const
const std::vector< uint8_t > & vector() const
void CreateTexture()
Creates the underlying SDL_Texture to be displayed.
void set_data(const std::vector< uint8_t > &data)
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap using SNES palette format.
SDL_Surface * surface() const
void Get16x16Tile(int tile_x, int tile_y, std::vector< uint8_t > &tile_data, int &tile_data_offset)
Extract a 16x16 tile from the bitmap (SNES metatile size)
Defines an abstract interface for all rendering operations.
RAII timer for automatic timing management.
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
SNES 16-bit tile metadata container.
void ComposeAndPlaceTilePart(Tilemap &tilemap, const std::vector< uint8_t > &data, const TileInfo &tile_info, int base_x, int base_y, int sheet_offset)
void MirrorTileDataVertically(std::vector< uint8_t > &tile_data)
void MirrorTileDataHorizontally(std::vector< uint8_t > &tile_data)
void RenderTile16(IRenderer *renderer, Tilemap &tilemap, int tile_id)
void ModifyTile16(Tilemap &tilemap, const std::vector< uint8_t > &data, const TileInfo &top_left, const TileInfo &top_right, const TileInfo &bottom_left, const TileInfo &bottom_right, int sheet_offset, int tile_id)
void UpdateTilemap(IRenderer *renderer, Tilemap &tilemap, const std::vector< uint8_t > &data)
void UpdateTile16(IRenderer *renderer, Tilemap &tilemap, int tile_id)
std::vector< uint8_t > GetTilemapData(Tilemap &tilemap, int tile_id)
Tilemap CreateTilemap(IRenderer *renderer, std::vector< uint8_t > &data, int width, int height, int tile_size, int num_tiles, SnesPalette &palette)
void RenderTilesBatch(IRenderer *renderer, Tilemap &tilemap, const std::vector< int > &tile_ids, const std::vector< std::pair< float, float > > &positions, const std::vector< std::pair< float, float > > &scales)
Render multiple tiles using atlas rendering for improved performance.
std::vector< uint8_t > FetchTileDataFromGraphicsBuffer(const std::vector< uint8_t > &data, int tile_id, int sheet_offset)
void RenderTile(IRenderer *renderer, Tilemap &tilemap, int tile_id)
void ComposeTile16(Tilemap &tilemap, const std::vector< uint8_t > &data, const TileInfo &top_left, const TileInfo &top_right, const TileInfo &bottom_left, const TileInfo &bottom_right, int sheet_offset)
int y
Y coordinate or height.
int x
X coordinate or width.
void CacheTile(int tile_id, const Bitmap &bitmap)
Cache a tile bitmap by copying it.
Bitmap * GetTile(int tile_id)
Get a cached tile by ID.
Tilemap structure for SNES tile-based graphics management.
Pair tile_size
Size of individual tiles (8x8 or 16x16)
TileCache tile_cache
Smart tile cache with LRU eviction.
Pair map_size
Size of tilemap in tiles.
Bitmap atlas
Master bitmap containing all tiles.
std::vector< std::array< gfx::TileInfo, 4 > > tile_info
Tile metadata (4 tiles per 16x16)