yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
graphics_scratch_view.cc
Go to the documentation of this file.
2
10#include "imgui/imgui.h"
11#include "imgui/misc/cpp/imgui_stdlib.h"
12#include "util/file_util.h"
13#include "util/macro.h"
14#include "util/platform_paths.h"
17
18namespace yaze::editor {
19namespace {
20
22 for (int i = 0; i < 16; ++i) {
23 const int level = i * 31 / ((1 << asset.bpp) - 1);
24 asset.palette[i] = std::min(level, 31) * 0x421;
25 }
26}
27
28} // namespace
29
31 public:
34 : owner_(owner), before_(std::move(before)), after_(std::move(after)) {}
35 absl::Status Undo() override { return Restore(before_); }
36 absl::Status Redo() override { return Restore(after_); }
37 std::string Description() const override {
38 return "Edit graphics scratch asset";
39 }
40 size_t MemoryUsage() const override {
41 return (before_.assets.size() + after_.assets.size()) * 4096;
42 }
43
44 private:
45 absl::Status Restore(const GraphicsScratchLibrary& library) {
46 // IDs are never reused, including after undoing a creation.
47 const uint64_t next_id = owner_->library_.next_id;
49 owner_->library_.next_id = std::max(next_id, library.next_id);
51 owner_->dirty_ = true;
52 return absl::OkStatus();
53 }
57};
58
60 UndoManager* history,
61 std::string library_path)
62 : state_(state), history_(history), path_(std::move(library_path)) {
63 Load();
64}
65
69
71 std::string label, ScratchPreviewCallback callback) {
72 const auto* asset = Selected();
73 preview_asset_id_ = asset ? asset->id : 0;
74 preview_label_ = std::move(label);
75 preview_callback_ = std::move(callback);
76 preview_hash_.reset();
77 preview_error_.clear();
79}
80
82 if (path_.empty()) {
84 if (!config.ok()) {
85 SetStatus(config.status());
86 return;
87 }
88 path_ = (*config / "GraphicsScratch.json").string();
89 }
91 if (!loaded.ok()) {
92 SetStatus(loaded.status());
93 return;
94 }
95 library_ = std::move(loaded->library);
96 existed_ = loaded->bytes.has_value();
97 loaded_bytes_ = loaded->bytes.value_or("");
98 load_ok_ = true;
99 if (!library_.assets.empty())
100 selected_id_ = library_.assets.front().id;
101}
102
103void GraphicsScratchView::SetStatus(absl::Status status) {
104 error_ = status.ok() ? "" : status.ToString();
105}
106
108 if (!dirty_)
109 return absl::OkStatus();
110 if (!load_ok_)
111 return absl::FailedPreconditionError(
112 "Existing scratch library was not loaded; it will not be replaced");
113 if (stroke_)
114 return absl::FailedPreconditionError(
115 "Finish the graphics scratch stroke before saving");
116 const auto expected =
117 existed_ ? std::optional<std::string>(loaded_bytes_) : std::nullopt;
119 path_, library_, expected));
120 existed_ = true;
121 dirty_ = false;
122 return absl::OkStatus();
123}
124
126 for (auto& asset : library_.assets)
127 if (asset.id == selected_id_)
128 return &asset;
129 return nullptr;
130}
131
133 dirty_ = true;
134 history_->Push(
135 std::make_unique<LibraryEdit>(this, std::move(before), library_));
136 const auto status = Flush();
137 SetStatus(status);
138 return status;
139}
140
142 if (!stroke_ || ImGui::IsMouseDown(ImGuiMouseButton_Left))
143 return;
144 auto before = std::move(*stroke_);
145 stroke_.reset();
146 Record(std::move(before));
147}
148
151 if (stroke_)
152 return absl::FailedPreconditionError(
153 "Finish the scratch stroke before changing assets");
154 return absl::OkStatus();
155}
156
158 for (auto& asset : library_.assets) {
159 const auto found = published_chunks_.find(
160 {asset.source_path, asset.source_offset, asset.bpp});
161 if (found != published_chunks_.end())
162 asset.original_chunk = found->second;
163 }
164}
165
168 if (!load_ok_)
169 return absl::FailedPreconditionError("Scratch library could not be loaded");
170 auto* store = ActiveSheetStore(*state_);
171 if (!store)
172 return absl::FailedPreconditionError(
173 "Load graphics in this ROM session first");
175 asset.name = asset_name_;
176 asset.bpp = bpp_;
177 const auto pixels = store->Sheet(state_->current_sheet_id);
178 asset.pixels.assign(pixels.begin(), pixels.end());
179 asset.source = "Sheet " + std::to_string(state_->current_sheet_id);
180 DefaultPalette(asset);
181 const auto& bitmap = gfx::Arena::Get().gfx_sheets()[state_->current_sheet_id];
182 if (const auto* target = state_->SourceTarget()) {
183 for (size_t i = 0; i < target->palette.size() && i < 16; ++i)
184 asset.palette[i] = target->palette[i].snes();
185 } else if (const auto* colors =
186 platform::GetSurfacePalette(bitmap.surface())) {
187 for (int i = 0; i < colors->ncolors && i < 8; ++i) {
188 const auto& c = colors->colors[i];
189 asset.palette[i] = (c.r >> 3) | ((c.g >> 3) << 5) | ((c.b >> 3) << 10);
190 }
191 }
192 auto before = library_;
193 auto status = library_.Add(std::move(asset));
194 if (!status.ok())
195 return status;
196 selected_id_ = library_.assets.back().id;
197 return Record(std::move(before));
198}
199
200absl::Status GraphicsScratchView::ImportBin(const std::string& path, int bpp,
201 size_t offset,
202 const gfx::SnesPalette& palette) {
204 if (!load_ok_)
205 return absl::FailedPreconditionError("Scratch library could not be loaded");
207 path, bpp, offset, asset_name_));
208 auto* asset = &imported;
209 DefaultPalette(*asset);
210 for (size_t i = 0; i < palette.size() && i < 16; ++i)
211 asset->palette[i] = palette[i].snes();
212 auto before = library_;
213 auto status = library_.Add(std::move(*asset));
214 if (!status.ok())
215 return status;
216 selected_id_ = library_.assets.back().id;
217 return Record(std::move(before));
218}
219
222 const auto* asset = Selected();
223 auto* store = ActiveSheetStore(*state_);
224 if (!asset || !store)
225 return absl::FailedPreconditionError(
226 "Select a scratch asset and load session graphics first");
229 std::any_of(asset->pixels.begin(), asset->pixels.end(),
230 [](uint8_t pixel) { return pixel > 7; }))
231 return absl::FailedPreconditionError(
232 "This ROM writer supports writable 3bpp sheets and indices 0-7; keep "
233 "other assets as BIN files");
234 if (apply_validator_) {
235 const auto status = apply_validator_(state_->current_sheet_id);
236 if (!status.ok())
237 return status;
238 }
239 const auto current = store->Sheet(state_->current_sheet_id);
240 auto diff =
241 MakeSheetPixelDiff(state_->current_sheet_id, current, asset->pixels);
242 if (diff.empty())
243 return absl::OkStatus();
244 auto status =
246 if (!status.ok())
247 return status;
248 history_->Push(std::make_unique<GraphicsPixelEditAction>(
249 state_, std::move(diff), "Apply graphics scratch asset"));
250 return absl::OkStatus();
251}
252
253absl::Status GraphicsScratchView::ExportBin(const std::string& path) {
255 auto* asset = Selected();
256 if (!asset)
257 return absl::FailedPreconditionError("Select a scratch asset");
259 *asset, path, path_, export_validator_));
260 if (result.same_source) {
261 published_chunks_[{asset->source_path, asset->source_offset, asset->bpp}] =
262 std::move(result.published_chunk);
264 dirty_ = true;
265 const auto saved = Flush();
266 if (!saved.ok())
267 return absl::Status(
268 saved.code(),
269 "BIN exported; updated scratch baseline remains unsaved: " +
270 std::string(saved.message()));
271 }
272 return absl::OkStatus();
273}
274
276 const std::vector<ScratchColorIdTarget>& targets,
277 const ColorIdPermutation& permutation) {
279 if (!load_ok_)
280 return absl::FailedPreconditionError("Scratch library could not be loaded");
282 library_, targets, permutation));
283 if (plan.edit.ranges.empty())
284 return absl::OkStatus();
285 auto next = library_;
287 auto before = std::move(library_);
288 library_ = std::move(next);
289 return Record(std::move(before));
290}
291
293 const GraphicsScratchAsset& selected) {
294 gui::AutoWidgetScope scratch_scope("Graphics/Scratch");
295 // SwapColorIds replaces the library that owns `selected`.
296 const uint64_t asset_id = selected.id;
297 const int bpp = selected.bpp;
298 const int last_id = (1 << bpp) - 1;
299 ImGui::TextUnformatted("Swap color IDs");
300 ImGui::SetNextItemWidth(120);
301 ImGui::InputInt("From ID##scratch_swap", &swap_from_id_, 0, 0);
302 gui::AutoRegisterLastItem("input", "Scratch swap from color ID");
303 ImGui::SameLine();
304 ImGui::SetNextItemWidth(120);
305 ImGui::InputInt("To ID##scratch_swap", &swap_to_id_, 0, 0);
306 gui::AutoRegisterLastItem("input", "Scratch swap to color ID");
307 swap_from_id_ = std::clamp(swap_from_id_, 1, last_id);
308 swap_to_id_ = std::clamp(swap_to_id_, 1, last_id);
309 ImGui::Checkbox("Every asset at this depth", &swap_all_at_depth_);
310 gui::AutoRegisterLastItem("checkbox", "Scratch swap every asset at depth");
311 ImGui::TextDisabled("ID 0 stays transparent; IDs 1-%d at %dbpp.", last_id,
312 bpp);
313 ImGui::BeginDisabled(stroke_.has_value() || swap_from_id_ == swap_to_id_);
314 if (ImGui::Button("Swap color IDs##scratch")) {
315 std::vector<ScratchColorIdTarget> targets;
316 for (const auto& candidate : library_.assets) {
317 if (candidate.id == asset_id ||
318 (swap_all_at_depth_ && candidate.bpp == bpp)) {
319 const auto whole = WholeScratchAssetSelection(candidate.id);
320 targets.insert(targets.end(), whole.begin(), whole.end());
321 }
322 }
323 const auto permutation = MakeColorIdSwap(bpp, swap_from_id_, swap_to_id_);
324 SetStatus(permutation.ok() ? SwapColorIds(targets, *permutation)
325 : permutation.status());
326 }
327 gui::AutoRegisterLastItem("button", "Scratch swap color IDs");
328 ImGui::EndDisabled();
329}
330
332 gui::AutoWidgetScope scratch_scope("Graphics/Scratch");
333 ImGui::SliderFloat("Zoom", &zoom_, 2, 8, "%.0fx");
334 color_ = std::clamp(color_, 0, (1 << asset.bpp) - 1);
335 ImGui::SliderInt("Color", &color_, 0, (1 << asset.bpp) - 1);
336 ImGui::BeginChild("ScratchPixels", ImVec2(0, 32 * zoom_ + 24), false,
337 ImGuiWindowFlags_HorizontalScrollbar);
338 const auto origin = ImGui::GetCursorScreenPos();
339 ImGui::InvisibleButton(
340 "Pixels", ImVec2(128 * zoom_, 32 * zoom_),
341 ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight);
342 gui::AutoRegisterLastItem("canvas", "ScratchPixels");
343 auto* draw = ImGui::GetWindowDrawList();
344 for (int y = 0; y < 32; ++y) {
345 for (int x = 0; x < 128; ++x) {
346 const uint16_t c = asset.palette[asset.pixels[y * 128 + x]];
347 const auto color =
348 IM_COL32((c & 31) * 255 / 31, ((c >> 5) & 31) * 255 / 31,
349 ((c >> 10) & 31) * 255 / 31, 255);
350 // Pixel cells must share hard edges at fractional zoom. ImGui 1.93
351 // antialiases AddRectFilled when its bounds are fractional.
352 draw->PrimReserve(6, 4);
353 draw->PrimRect(
354 ImVec2(origin.x + x * zoom_, origin.y + y * zoom_),
355 ImVec2(origin.x + (x + 1) * zoom_, origin.y + (y + 1) * zoom_),
356 color);
357 }
358 }
359 if (ImGui::IsItemHovered()) {
360 const auto mouse = ImGui::GetMousePos();
361 const int x = static_cast<int>((mouse.x - origin.x) / zoom_);
362 const int y = static_cast<int>((mouse.y - origin.y) / zoom_);
363 if (x >= 0 && x < 128 && y >= 0 && y < 32) {
364 auto& pixel = asset.pixels[y * 128 + x];
365 if (ImGui::IsMouseClicked(ImGuiMouseButton_Right))
366 color_ = pixel;
367 if (ImGui::IsMouseDown(ImGuiMouseButton_Left) && pixel != color_) {
368 if (!stroke_)
370 pixel = color_;
371 }
372 }
373 }
374 ImGui::EndChild();
375}
376
378 const auto* asset = Selected();
379 if (!asset || asset->id != preview_asset_id_ || !preview_callback_)
380 return;
381 uint64_t hash = 1469598103934665603ULL;
382 for (const auto pixel : asset->pixels)
383 hash = (hash ^ pixel) * 1099511628211ULL;
384 for (const auto color : asset->palette)
385 hash = (hash ^ color) * 1099511628211ULL;
386 if (!preview_hash_ || *preview_hash_ != hash) {
387 preview_hash_ = hash;
388 auto image = preview_callback_(*asset);
389 if (!image.ok()) {
390 preview_error_ = std::string(image.status().message());
391 } else {
392 const bool indexed =
394 const size_t bytes_per_pixel = indexed ? 1 : 4;
395 if (image->width <= 0 || image->height <= 0 || image->width > 1024 ||
396 image->height > 1024 ||
397 image->pixels.size() != static_cast<size_t>(image->width) *
398 image->height * bytes_per_pixel) {
399 preview_error_ = "Associated preview returned invalid image dimensions";
400 } else {
401 preview_error_.clear();
402 const int depth = indexed ? 8 : 32;
403 const bool create = !preview_bitmap_.is_active() ||
404 preview_bitmap_.width() != image->width ||
405 preview_bitmap_.height() != image->height ||
406 preview_bitmap_.depth() != depth;
407 if (create)
408 preview_bitmap_.Create(image->width, image->height, depth,
409 image->pixels);
410 else
411 preview_bitmap_.set_data(image->pixels);
412 if (indexed) {
413 std::vector<SDL_Color> colors(image->colors.begin(),
414 image->colors.end());
415 if (image->index0_transparent)
416 colors[0].a = 0;
418 }
425 }
426 }
427 }
428 ImGui::Separator();
429 ImGui::TextWrapped("Associated sprite preview: %s", preview_label_.c_str());
430 ImGui::TextDisabled(
431 "Captured pose; scratch pixels update before BIN export.");
432 if (!preview_error_.empty()) {
433 ImGui::TextWrapped("Preview unavailable: %s", preview_error_.c_str());
434 return;
435 }
437 return;
438 const float scale = std::clamp(
439 ImGui::GetContentRegionAvail().x / preview_bitmap_.width(), 0.25f, 2.0f);
441 options.canvas_size = ImVec2(preview_bitmap_.width() * scale + 4,
442 preview_bitmap_.height() * scale + 4);
443 options.draw_grid = false;
444 options.draw_context_menu = false;
445 options.draw_overlay = false;
446 options.render_popups = false;
447 options.use_child_window = false;
448 gui::BeginChildWithScrollbar("ScratchAssociatedPreview", options.canvas_size);
449 auto canvas = gui::BeginCanvas(preview_canvas_, options);
451 gui::EndCanvas(preview_canvas_, canvas, options);
452 ImGui::EndChild();
453}
454
455void GraphicsScratchView::Draw(bool* p_open) {
456 (void)p_open;
458 ImGui::TextWrapped(
459 "Keep named sheets here until you choose their destination. Left drag "
460 "paints; right click picks a color. Scratch assets retain their own "
461 "pixels and palette.");
462 ImGui::TextWrapped("Library: %s", path_.c_str());
463 if (!error_.empty())
464 ImGui::TextWrapped("%s", error_.c_str());
465 ImGui::BeginDisabled(!load_ok_ || stroke_.has_value());
466 ImGui::InputText("Asset name", &asset_name_);
467 ImGui::SliderInt("SNES bpp", &bpp_, 2, 4);
468 if (ImGui::Button("New blank")) {
469 auto before = library_;
471 asset.name = asset_name_;
472 asset.bpp = bpp_;
473 DefaultPalette(asset);
474 const auto status = library_.Add(std::move(asset));
475 if (status.ok()) {
476 selected_id_ = library_.assets.back().id;
477 Record(std::move(before));
478 } else {
479 SetStatus(status);
480 }
481 }
482 ImGui::SameLine();
483 if (ImGui::Button("Keep current sheet"))
485 ImGui::InputText("BIN path", &bin_path_);
486 ImGui::InputInt("BIN byte offset", &bin_offset_);
487 bin_offset_ = std::max(0, bin_offset_);
488 if (ImGui::Button("Import 64 tiles from BIN")) {
489 auto path = bin_path_;
490 if (path.empty())
492 if (!path.empty())
494 }
495 if (ImGui::BeginCombo("Saved assets", Selected() ? Selected()->name.c_str()
496 : "Choose asset")) {
497 for (const auto& asset : library_.assets) {
498 ImGui::PushID(static_cast<int>(asset.id));
499 if (ImGui::Selectable(asset.name.c_str(), asset.id == selected_id_))
500 selected_id_ = asset.id;
501 ImGui::PopID();
502 }
503 ImGui::EndCombo();
504 }
505 ImGui::EndDisabled();
506 auto* asset = Selected();
507 if (!asset)
508 return;
509 ImGui::Text("%s (%d bpp) | %s", asset->name.c_str(), asset->bpp,
510 asset->source.c_str());
511 ImGui::BeginDisabled(stroke_.has_value());
512 if (ImGui::Button("Rename to asset name")) {
513 auto candidate = *asset;
514 candidate.name = asset_name_;
515 if (auto status = candidate.Validate(); status.ok()) {
516 auto before = library_;
517 asset->name = asset_name_;
518 Record(std::move(before));
519 } else {
520 SetStatus(status);
521 }
522 }
523 ImGui::SameLine();
524 if (ImGui::Button("Duplicate")) {
525 auto before = library_;
526 auto copy = *asset;
527 copy.name = asset_name_;
528 const auto status = library_.Add(std::move(copy));
529 if (status.ok()) {
530 selected_id_ = library_.assets.back().id;
531 Record(std::move(before));
532 } else {
533 SetStatus(status);
534 }
535 }
536 // Add may reallocate the vector.
537 asset = Selected();
538 ImGui::SameLine();
539 if (ImGui::Button("Remove asset")) {
540 auto before = library_;
541 const auto id = selected_id_;
542 library_.assets.erase(
543 std::remove_if(library_.assets.begin(), library_.assets.end(),
544 [id](const auto& entry) { return entry.id == id; }),
545 library_.assets.end());
546 selected_id_ = library_.assets.empty() ? 0 : library_.assets.front().id;
547 Record(std::move(before));
548 ImGui::EndDisabled();
549 return;
550 }
551 if (ImGui::Button("Apply pixels to current sheet"))
553 ImGui::SameLine();
554 if (ImGui::Button("Export BIN")) {
555 const auto path =
557 if (!path.empty())
558 SetStatus(ExportBin(path));
559 }
560 ImGui::SameLine();
561 if (ImGui::Button("Save scratch library"))
562 SetStatus(Flush());
563 ImGui::EndDisabled();
564 DrawColorIdSwap(*asset);
565 // Swapping replaces the library; reacquire the selection before painting.
566 asset = Selected();
567 if (!asset)
568 return;
569 DrawPixels(*asset);
571}
572
573} // namespace yaze::editor
Shared state between GraphicsEditor panel components.
const GraphicsEditTarget * SourceTarget() const
absl::Status Restore(const GraphicsScratchLibrary &library)
LibraryEdit(GraphicsScratchView *owner, GraphicsScratchLibrary before, GraphicsScratchLibrary after)
size_t MemoryUsage() const override
Approximate memory footprint for budget enforcement.
std::string Description() const override
Human-readable description (e.g., "Paint 12 tiles on map 5")
void SetAssociatedPreview(std::string label, ScratchPreviewCallback callback)
void DrawPixels(GraphicsScratchAsset &asset)
void Draw(bool *p_open) override
Draw the panel content.
std::optional< GraphicsScratchLibrary > stroke_
std::function< absl::Status(const std::string &) export_validator_)
absl::Status ImportBin(const std::string &path, int bpp, size_t offset, const gfx::SnesPalette &palette={})
const GraphicsScratchLibrary & library() const
void DrawColorIdSwap(const GraphicsScratchAsset &selected)
absl::Status SwapColorIds(const std::vector< ScratchColorIdTarget > &targets, const ColorIdPermutation &permutation)
GraphicsScratchView(GraphicsEditorState *state, UndoManager *history, std::string library_path="")
std::optional< uint64_t > preview_hash_
absl::Status Record(GraphicsScratchLibrary before)
absl::Status ExportBin(const std::string &path)
std::map< std::tuple< std::string, uint64_t, int >, std::vector< uint8_t > > published_chunks_
std::function< absl::Status(uint16_t)> apply_validator_
Abstract base for all undoable actions (Command pattern)
Definition undo_action.h:20
Manages undo/redo stacks for a single editor.
void Push(std::unique_ptr< UndoAction > action)
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
Definition arena.cc:39
void RetireBitmap(Bitmap &bitmap)
Explicitly retire resources owned by a bitmap that is being erased.
Definition arena.cc:63
GraphicsSheets & gfx_sheets()
Definition arena.h:178
static Arena & Get()
Definition arena.cc:24
void Create(int width, int height, int depth, std::span< uint8_t > data)
Create a bitmap with the given dimensions and data.
Definition bitmap.cc:198
BitmapMetadata & metadata()
Definition bitmap.h:395
bool is_active() const
Definition bitmap.h:414
int height() const
Definition bitmap.h:399
void set_data(const std::vector< uint8_t > &data)
Definition bitmap.cc:901
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap using SNES palette format.
Definition bitmap.cc:452
int width() const
Definition bitmap.h:398
int depth() const
Definition bitmap.h:400
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
RAII scope that enables automatic widget registration.
void DrawBitmap(Bitmap &bitmap, int border_offset, float scale)
Definition canvas.cc:1295
static std::string ShowSaveFileDialog(const std::string &default_name="", const std::string &default_extension="")
ShowSaveFileDialog opens a save file dialog and returns the selected filepath. Uses global feature fl...
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath. Uses global feature flag to...
static absl::StatusOr< std::filesystem::path > GetConfigDirectory()
Get the user-specific configuration directory for YAZE.
Locates graphics sheets in the ROM and writes edited sheets back.
One ROM session's graphics sheet pixels, with a revision per sheet.
The Graphics editor's path for sheet pixel writes.
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition macro.h:62
absl::StatusOr< LibrarySnapshot > LoadLibrary(const std::string &path)
absl::StatusOr< BinExportResult > ExportBin(const GraphicsScratchAsset &asset, const std::string &path, const std::string &library_path, const std::function< absl::Status(const std::string &)> &validator)
absl::StatusOr< std::string > SaveLibrary(const std::string &path, const GraphicsScratchLibrary &library, const std::optional< std::string > &expected_bytes)
absl::StatusOr< GraphicsScratchAsset > ImportBin(const std::string &path, int bpp, size_t offset, const std::string &name)
Editors are the view controllers for the application.
zelda3::GraphicsSheetStore * ActiveSheetStore(const GraphicsEditorState &state)
std::vector< ScratchColorIdTarget > WholeScratchAssetSelection(uint64_t asset_id)
absl::StatusOr< ColorIdPermutation > MakeColorIdSwap(int bpp, int from, int to)
absl::Status CommitSheetPixels(GraphicsEditorState &state, uint16_t sheet, absl::Span< const uint8_t > pixels)
absl::StatusOr< ScratchColorIdPlan > PlanScratchColorIdPermutation(const GraphicsScratchLibrary &library, const std::vector< ScratchColorIdTarget > &targets, const ColorIdPermutation &permutation)
absl::Status ApplyScratchColorIdPlan(GraphicsScratchLibrary &library, const ScratchColorIdPlan &plan)
std::function< absl::StatusOr< usage_preview::PreviewImage >( const GraphicsScratchAsset &)> ScratchPreviewCallback
SheetPixelDiff MakeSheetPixelDiff(uint16_t sheet, absl::Span< const uint8_t > before, absl::Span< const uint8_t > after)
The pixels that differ between two 4096-byte sheets.
void EndCanvas(Canvas &canvas)
void AutoRegisterLastItem(const std::string &widget_type, const std::string &explicit_label, const std::string &description)
Automatically register the last ImGui item.
void BeginCanvas(Canvas &canvas, ImVec2 child_size)
void BeginChildWithScrollbar(const char *str_id)
Definition style.cc:315
SDL_Palette * GetSurfacePalette(SDL_Surface *surface)
Get the palette attached to a surface.
Definition sdl_compat.h:398
GfxSheetStorageKind GetGfxSheetStorageKind(uint16_t sheet_id)
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22
absl::Status Add(GraphicsScratchAsset asset)
std::vector< GraphicsScratchAsset > assets
Automatic widget registration helpers for ImGui Test Engine integration.