yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
sprite_drawer.cc
Go to the documentation of this file.
2
3#include <algorithm>
4
6
7namespace yaze {
8namespace editor {
9
10SpriteDrawer::SpriteDrawer(const uint8_t* sprite_gfx_buffer)
11 : sprite_gfx_(sprite_gfx_buffer) {}
12
14 if (!bitmap.is_active())
15 return;
16
17 auto& data = bitmap.mutable_data();
18 std::fill(data.begin(), data.end(), 0);
19}
20
22 const zsprite::OamTile& tile, int origin_x,
23 int origin_y) {
24 if (!sprite_gfx_)
25 return;
26 if (!bitmap.is_active())
27 return;
28
29 // ZSM stores canvas coordinates; its exporter subtracts (128, 112).
30 int signed_x = sprite_authoring::OffsetX(tile);
31 int signed_y = sprite_authoring::OffsetY(tile);
32
33 int dest_x = origin_x + signed_x;
34 int dest_y = origin_y + signed_y;
35
36 if (tile.size) {
37 // 16x16 mode
38 DrawTile16x16(bitmap, tile.id, dest_x, dest_y, tile.mirror_x, tile.mirror_y,
39 tile.palette);
40 } else {
41 // 8x8 mode
42 DrawTile8x8(bitmap, tile.id, dest_x, dest_y, tile.mirror_x, tile.mirror_y,
43 tile.palette);
44 }
45}
46
48 int origin_x, int origin_y) {
49 // Draw tiles in reverse order (first in list = top priority)
50 // This ensures proper layering with later tiles drawn on top
51 for (auto it = frame.Tiles.rbegin(); it != frame.Tiles.rend(); ++it) {
52 DrawOamTile(bitmap, *it, origin_x, origin_y);
53 }
54}
55
56uint8_t SpriteDrawer::GetTilePixel(uint16_t tile_id, int px, int py) const {
57 if (!sprite_gfx_)
58 return 0;
59 if (tile_id > kMaxTileId)
60 return 0;
61 if (px < 0 || px >= kTileSize || py < 0 || py >= kTileSize)
62 return 0;
63
64 // Calculate position in 8BPP linear buffer
65 // Layout: 16 tiles per row, each tile 8x8 pixels
66 // Row stride: 128 bytes (16 tiles * 8 bytes)
67 int tile_col = tile_id % kTilesPerRow;
68 int tile_row = tile_id / kTilesPerRow;
69
70 int base_x = tile_col * kTileSize;
71 int base_y = tile_row * kTileRowSize; // 1024 bytes per tile row
72
73 int src_index = base_y + (py * kRowStride) + base_x + px;
74
75 // Bounds check against typical buffer size (0x10000)
76 if (src_index >= 0x10000)
77 return 0;
78
79 return sprite_gfx_[src_index];
80}
81
82void SpriteDrawer::DrawTile8x8(gfx::Bitmap& bitmap, uint16_t tile_id, int x,
83 int y, bool flip_x, bool flip_y,
84 uint8_t palette) {
85 if (!sprite_gfx_)
86 return;
87 if (tile_id > kMaxTileId)
88 return;
89
90 // Sprite palettes use 16 colors each (including transparent)
91 // Palette index 0-7 map to colors 0-127 in the combined palette
92 uint8_t palette_offset = (palette & 0x07) * 16;
93
94 for (int py = 0; py < kTileSize; py++) {
95 int src_py = flip_y ? (kTileSize - 1 - py) : py;
96
97 for (int px = 0; px < kTileSize; px++) {
98 int src_px = flip_x ? (kTileSize - 1 - px) : px;
99
100 uint8_t pixel = GetTilePixel(tile_id, src_px, src_py);
101
102 // Pixel 0 is transparent
103 if (pixel != 0) {
104 int dest_x = x + px;
105 int dest_y = y + py;
106
107 if (dest_x >= 0 && dest_x < bitmap.width() && dest_y >= 0 &&
108 dest_y < bitmap.height()) {
109 int dest_index = dest_y * bitmap.width() + dest_x;
110 if (dest_index >= 0 &&
111 dest_index < static_cast<int>(bitmap.mutable_data().size())) {
112 // Map pixel to palette: pixel value + palette offset
113 // Pixel 1 -> palette_offset, pixel 2 -> palette_offset+1, etc.
114 bitmap.mutable_data()[dest_index] = pixel + palette_offset;
115 }
116 }
117 }
118 }
119 }
120}
121
122void SpriteDrawer::DrawTile16x16(gfx::Bitmap& bitmap, uint16_t tile_id, int x,
123 int y, bool flip_x, bool flip_y,
124 uint8_t palette) {
125 // 16x16 tile is composed of 4 8x8 tiles in a 2x2 grid:
126 // [base + 0] [base + 1]
127 // [base + 16] [base + 17]
128 //
129 // When mirrored horizontally:
130 // [base + 1] [base + 0]
131 // [base + 17] [base + 16]
132 //
133 // When mirrored vertically:
134 // [base + 16] [base + 17]
135 // [base + 0] [base + 1]
136 //
137 // When mirrored both:
138 // [base + 17] [base + 16]
139 // [base + 1] [base + 0]
140
141 uint16_t tl = tile_id; // Top-left
142 uint16_t tr = tile_id + 1; // Top-right
143 uint16_t bl = tile_id + 16; // Bottom-left
144 uint16_t br = tile_id + 17; // Bottom-right
145
146 // Swap tiles based on mirroring
147 if (flip_x) {
148 std::swap(tl, tr);
149 std::swap(bl, br);
150 }
151 if (flip_y) {
152 std::swap(tl, bl);
153 std::swap(tr, br);
154 }
155
156 // Draw the 4 component tiles
157 DrawTile8x8(bitmap, tl, x, y, flip_x, flip_y, palette);
158 DrawTile8x8(bitmap, tr, x + 8, y, flip_x, flip_y, palette);
159 DrawTile8x8(bitmap, bl, x, y + 8, flip_x, flip_y, palette);
160 DrawTile8x8(bitmap, br, x + 8, y + 8, flip_x, flip_y, palette);
161}
162
163} // namespace editor
164} // namespace yaze
void ClearBitmap(gfx::Bitmap &bitmap)
Clear the bitmap with transparent color.
uint8_t GetTilePixel(uint16_t tile_id, int px, int py) const
Get pixel value from graphics buffer.
void DrawFrame(gfx::Bitmap &bitmap, const zsprite::Frame &frame, int origin_x, int origin_y)
Draw all tiles in a ZSM frame.
static constexpr int kRowStride
static constexpr int kTileRowSize
void DrawOamTile(gfx::Bitmap &bitmap, const zsprite::OamTile &tile, int origin_x, int origin_y)
Draw a single ZSM OAM tile to bitmap.
static constexpr int kMaxTileId
static constexpr int kTilesPerRow
void DrawTile16x16(gfx::Bitmap &bitmap, uint16_t tile_id, int x, int y, bool flip_x, bool flip_y, uint8_t palette)
Draw a 16x16 tile (4 8x8 tiles) to bitmap.
static constexpr int kTileSize
void DrawTile8x8(gfx::Bitmap &bitmap, uint16_t tile_id, int x, int y, bool flip_x, bool flip_y, uint8_t palette)
Draw an 8x8 tile to bitmap.
SpriteDrawer(const uint8_t *sprite_gfx_buffer=nullptr)
Construct a SpriteDrawer with graphics buffer.
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:70
bool is_active() const
Definition bitmap.h:414
int height() const
Definition bitmap.h:399
int width() const
Definition bitmap.h:398
std::vector< uint8_t > & mutable_data()
Definition bitmap.h:408
int OffsetX(const zsprite::OamTile &tile)
int OffsetY(const zsprite::OamTile &tile)
std::vector< OamTile > Tiles
Definition zsprite.h:112