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
5namespace yaze {
6namespace editor {
7
8SpriteDrawer::SpriteDrawer(const uint8_t* sprite_gfx_buffer)
9 : sprite_gfx_(sprite_gfx_buffer) {}
10
12 if (!bitmap.is_active()) return;
13
14 auto& data = bitmap.mutable_data();
15 std::fill(data.begin(), data.end(), 0);
16}
17
19 const zsprite::OamTile& tile, int origin_x,
20 int origin_y) {
21 if (!sprite_gfx_) return;
22 if (!bitmap.is_active()) return;
23
24 // OAM tile positions are signed 8-bit values relative to sprite origin
25 // In ZSM, x and y are uint8_t but represent signed positions
26 int8_t signed_x = static_cast<int8_t>(tile.x);
27 int8_t signed_y = static_cast<int8_t>(tile.y);
28
29 int dest_x = origin_x + signed_x;
30 int dest_y = origin_y + signed_y;
31
32 if (tile.size) {
33 // 16x16 mode
34 DrawTile16x16(bitmap, tile.id, dest_x, dest_y, tile.mirror_x, tile.mirror_y,
35 tile.palette);
36 } else {
37 // 8x8 mode
38 DrawTile8x8(bitmap, tile.id, dest_x, dest_y, tile.mirror_x, tile.mirror_y,
39 tile.palette);
40 }
41}
42
44 int origin_x, int origin_y) {
45 // Draw tiles in reverse order (first in list = top priority)
46 // This ensures proper layering with later tiles drawn on top
47 for (auto it = frame.Tiles.rbegin(); it != frame.Tiles.rend(); ++it) {
48 DrawOamTile(bitmap, *it, origin_x, origin_y);
49 }
50}
51
52uint8_t SpriteDrawer::GetTilePixel(uint16_t tile_id, int px, int py) const {
53 if (!sprite_gfx_) return 0;
54 if (tile_id > kMaxTileId) return 0;
55 if (px < 0 || px >= kTileSize || py < 0 || py >= kTileSize) return 0;
56
57 // Calculate position in 8BPP linear buffer
58 // Layout: 16 tiles per row, each tile 8x8 pixels
59 // Row stride: 128 bytes (16 tiles * 8 bytes)
60 int tile_col = tile_id % kTilesPerRow;
61 int tile_row = tile_id / kTilesPerRow;
62
63 int base_x = tile_col * kTileSize;
64 int base_y = tile_row * kTileRowSize; // 1024 bytes per tile row
65
66 int src_index = base_y + (py * kRowStride) + base_x + px;
67
68 // Bounds check against typical buffer size (0x10000)
69 if (src_index >= 0x10000) return 0;
70
71 return sprite_gfx_[src_index];
72}
73
74void SpriteDrawer::DrawTile8x8(gfx::Bitmap& bitmap, uint16_t tile_id, int x,
75 int y, bool flip_x, bool flip_y,
76 uint8_t palette) {
77 if (!sprite_gfx_) return;
78 if (tile_id > kMaxTileId) return;
79
80 // Sprite palettes use 16 colors each (including transparent)
81 // Palette index 0-7 map to colors 0-127 in the combined palette
82 uint8_t palette_offset = (palette & 0x07) * 16;
83
84 for (int py = 0; py < kTileSize; py++) {
85 int src_py = flip_y ? (kTileSize - 1 - py) : py;
86
87 for (int px = 0; px < kTileSize; px++) {
88 int src_px = flip_x ? (kTileSize - 1 - px) : px;
89
90 uint8_t pixel = GetTilePixel(tile_id, src_px, src_py);
91
92 // Pixel 0 is transparent
93 if (pixel != 0) {
94 int dest_x = x + px;
95 int dest_y = y + py;
96
97 if (dest_x >= 0 && dest_x < bitmap.width() && dest_y >= 0 &&
98 dest_y < bitmap.height()) {
99 int dest_index = dest_y * bitmap.width() + dest_x;
100 if (dest_index >= 0 &&
101 dest_index < static_cast<int>(bitmap.mutable_data().size())) {
102 // Map pixel to palette: pixel value + palette offset
103 // Pixel 1 -> palette_offset, pixel 2 -> palette_offset+1, etc.
104 bitmap.mutable_data()[dest_index] = pixel + palette_offset;
105 }
106 }
107 }
108 }
109 }
110}
111
112void SpriteDrawer::DrawTile16x16(gfx::Bitmap& bitmap, uint16_t tile_id, int x,
113 int y, bool flip_x, bool flip_y,
114 uint8_t palette) {
115 // 16x16 tile is composed of 4 8x8 tiles in a 2x2 grid:
116 // [base + 0] [base + 1]
117 // [base + 16] [base + 17]
118 //
119 // When mirrored horizontally:
120 // [base + 1] [base + 0]
121 // [base + 17] [base + 16]
122 //
123 // When mirrored vertically:
124 // [base + 16] [base + 17]
125 // [base + 0] [base + 1]
126 //
127 // When mirrored both:
128 // [base + 17] [base + 16]
129 // [base + 1] [base + 0]
130
131 uint16_t tl = tile_id; // Top-left
132 uint16_t tr = tile_id + 1; // Top-right
133 uint16_t bl = tile_id + 16; // Bottom-left
134 uint16_t br = tile_id + 17; // Bottom-right
135
136 // Swap tiles based on mirroring
137 if (flip_x) {
138 std::swap(tl, tr);
139 std::swap(bl, br);
140 }
141 if (flip_y) {
142 std::swap(tl, bl);
143 std::swap(tr, br);
144 }
145
146 // Draw the 4 component tiles
147 DrawTile8x8(bitmap, tl, x, y, flip_x, flip_y, palette);
148 DrawTile8x8(bitmap, tr, x + 8, y, flip_x, flip_y, palette);
149 DrawTile8x8(bitmap, bl, x, y + 8, flip_x, flip_y, palette);
150 DrawTile8x8(bitmap, br, x + 8, y + 8, flip_x, flip_y, palette);
151}
152
153} // namespace editor
154} // 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:67
bool is_active() const
Definition bitmap.h:384
int height() const
Definition bitmap.h:374
int width() const
Definition bitmap.h:373
std::vector< uint8_t > & mutable_data()
Definition bitmap.h:378
std::vector< OamTile > Tiles
Definition zsprite.h:107