yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
sprite_graphics_context.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <array>
5#include <set>
6#include <string>
7#include <vector>
8
9#include "absl/strings/str_format.h"
14#include "zelda3/game_data.h"
17
19
20// Capture one OAM entry in one selected ZSM frame. A 16x16 entry may cross a
21// sheet slot, so resolve and validate each 8x8 tile independently.
22absl::StatusOr<GraphicsEditContext> Make(const zsprite::Frame& frame,
23 int frame_index, int oam_index,
24 const std::array<uint8_t, 8>& slots,
25 const gfx::PaletteGroup& palettes,
26 const zelda3::GameData* owner,
27 const std::string& source_label,
28 const std::string& sheet_source) {
29 if (!owner || oam_index < 0 ||
30 oam_index >= static_cast<int>(frame.Tiles.size())) {
31 return absl::InvalidArgumentError("Select a sprite frame and OAM tile");
32 }
33 const auto& oam = frame.Tiles[oam_index];
34 if (oam.palette >= palettes.size() ||
35 palettes.palette_ref(oam.palette).size() < 8) {
36 return absl::FailedPreconditionError("Selected OAM palette is unresolved");
37 }
38 const auto& row = palettes.palette_ref(oam.palette);
39 std::vector<gfx::SnesColor> colors;
40 for (int i = 0; i < 8; ++i)
41 colors.push_back(row[i]);
42 GraphicsEditContext context;
43 context.owner = owner;
44 context.label =
45 absl::StrFormat("%s / frame %d / OAM %d / palette %d", source_label,
46 frame_index, oam_index, oam.palette);
47 context.limitation =
48 "Captured pose and sheet assignments; re-enter from Sprite Editor after "
49 "changing either.";
50 context.resolved.surface = "sprite_oam";
51 context.resolved.graphics_loaded = true;
52 context.resolved.id = frame_index;
53 context.resolved.palette_colors_resolved = true;
54 context.resolved.loader_agrees = true;
55 context.resolved.palettes.push_back(
56 {absl::StrFormat("OAM palette row %d", oam.palette), sheet_source,
57 oam.palette, false});
58 for (int slot = 0; slot < 8; ++slot) {
59 const uint16_t sheet = slots[slot];
60 const bool loaded = owner->sheet_store.HasSheet(sheet);
61 context.resolved.slots.push_back({
62 .slot = slot,
63 .role = slot < 4 ? "static_sprite" : "spriteset_sprite",
64 .palette_group = absl::StrFormat("sprite_row_%d", oam.palette),
65 .sheet_id = sheet,
66 .source = sheet_source,
67 .loader_agrees = loaded,
68 .store_revision = owner->sheet_store.Revision(sheet),
69 .width = 128,
70 .height = 32,
71 });
72 context.resolved.loader_agrees &= loaded;
73 }
74 const int offsets[] = {0, 1, 16, 17};
75 const int count = oam.size ? 4 : 1;
76 for (int i = 0; i < count; ++i) {
77 const int tile = static_cast<int>(oam.id) + offsets[i];
78 if (tile < 0 || tile >= 512) {
79 return absl::OutOfRangeError("Selected OAM tile extends past tile 0x1FF");
80 }
81 const int slot = zelda3::SpriteSlotForTile(tile);
82 const uint16_t sheet = slots[slot];
85 !owner->sheet_store.HasSheet(sheet)) {
86 return absl::FailedPreconditionError(absl::StrFormat(
87 "OAM tile %03X uses unavailable or 2bpp sheet %02X", tile, sheet));
88 }
89 context.targets.push_back({
90 .sheet_id = sheet,
91 .tile_index = static_cast<uint16_t>(tile % 64),
92 .label = absl::StrFormat(
93 "Frame %d / OAM %d / tile %03X / slot %d / sheet %02X / palette %d",
94 frame_index, oam_index, tile, slot, sheet, oam.palette),
95 .palette = gfx::SnesPalette(colors),
96 });
97 }
98 return context;
99}
100
101// The frame, assignments and colors are captured at navigation time; pixels
102// always come from the owning session's sheet store, including unsaved strokes.
103absl::StatusOr<usage_preview::PreviewImage> RenderCapturedFrame(
104 const zsprite::Frame& frame, const std::array<uint8_t, 8>& slots,
105 const gfx::PaletteGroup& palettes, const zelda3::GameData* owner) {
106 if (!owner)
107 return absl::FailedPreconditionError("Sprite ROM session ended");
108 std::set<uint16_t> ids(slots.begin(), slots.end());
109 const auto sheets = usage_preview::SnapshotSessionSheets(ids, owner);
110 std::vector<uint8_t> buffer(0x10000, 0);
111 for (size_t slot = 0; slot < slots.size(); ++slot) {
112 const auto it = sheets.find(slots[slot]);
113 if (it == sheets.end() || it->second.size() != 4096) {
114 return absl::FailedPreconditionError(
115 "Sprite sheet unavailable in source session");
116 }
117 std::copy(it->second.begin(), it->second.end(),
118 buffer.begin() + slot * 4096);
119 }
120 return RenderFrameBuffer(frame, buffer, palettes);
121}
122
123absl::StatusOr<usage_preview::PreviewImage> RenderFrameBuffer(
124 const zsprite::Frame& frame, std::span<const uint8_t> buffer,
125 const gfx::PaletteGroup& palettes) {
126 if (buffer.size() < 8 * 4096)
127 return absl::InvalidArgumentError(
128 "Sprite frame needs eight complete graphics slots");
129 constexpr int kSize = 256;
130 gfx::Bitmap canvas(kSize, kSize, 8, std::vector<uint8_t>(kSize * kSize, 0));
131 SpriteDrawer drawer(buffer.data());
132 drawer.DrawFrame(canvas, frame, 128, 128);
134 image.width = kSize;
135 image.height = kSize;
136 image.pixels = canvas.vector();
138 image.index0_transparent = true;
139 for (size_t row = 0; row < palettes.size() && row < 8; ++row) {
140 const auto& palette = palettes.palette_ref(row);
141 for (size_t index = 0; index < palette.size() && index < 16; ++index) {
142 const auto rgb = palette[index].rgb();
143 image.colors[row * 16 + index] = {static_cast<uint8_t>(rgb.x),
144 static_cast<uint8_t>(rgb.y),
145 static_cast<uint8_t>(rgb.z), 255};
146 }
147 }
148 return image;
149}
150
151} // namespace yaze::editor::sprite_graphics_context
Draws sprite OAM tiles to bitmaps for preview rendering.
void DrawFrame(gfx::Bitmap &bitmap, const zsprite::Frame &frame, int origin_x, int origin_y)
Draw all tiles in a ZSM frame.
void RetireBitmap(Bitmap &bitmap)
Explicitly retire resources owned by a bitmap that is being erased.
Definition arena.cc:63
static Arena & Get()
Definition arena.cc:24
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:70
const std::vector< uint8_t > & vector() const
Definition bitmap.h:411
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
bool HasSheet(uint16_t sheet) const
True when the buffer holds all 4096 bytes of sheet.
uint64_t Revision(uint16_t sheet) const
Locates graphics sheets in the ROM and writes edited sheets back.
absl::StatusOr< GraphicsEditContext > Make(const zsprite::Frame &frame, int frame_index, int oam_index, const std::array< uint8_t, 8 > &slots, const gfx::PaletteGroup &palettes, const zelda3::GameData *owner, const std::string &source_label, const std::string &sheet_source)
absl::StatusOr< usage_preview::PreviewImage > RenderCapturedFrame(const zsprite::Frame &frame, const std::array< uint8_t, 8 > &slots, const gfx::PaletteGroup &palettes, const zelda3::GameData *owner)
absl::StatusOr< usage_preview::PreviewImage > RenderFrameBuffer(const zsprite::Frame &frame, std::span< const uint8_t > buffer, const gfx::PaletteGroup &palettes)
SheetPixels SnapshotSessionSheets(const std::set< uint16_t > &ids, const zelda3::GameData *game_data)
GfxSheetStorageKind GetGfxSheetStorageKind(uint16_t sheet_id)
constexpr int SpriteSlotForTile(int tile)
Slot (0-7) holding 8x8 OAM tile tile (name-table bit included).
Which graphics sheets back sprite OAM tiles 0x000-0x1FF.
zelda3::ResolvedGraphicsContext resolved
std::vector< GraphicsEditTarget > targets
std::vector< OamTile > Tiles
Definition zsprite.h:112
Represents a group of palettes.
const SnesPalette & palette_ref(int i) const
std::vector< ResolvedPaletteSource > palettes
std::vector< ResolvedGraphicsSlot > slots
Renders a graphics sheet the way the game uses it, from unsaved sheet pixels, without writing the ROM...