yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
canvas_utils.cc
Go to the documentation of this file.
1#include "canvas_utils.h"
2
3#include <cmath>
6#include "util/log.h"
7
8namespace yaze {
9namespace gui {
10namespace CanvasUtils {
11
12ImVec2 AlignToGrid(ImVec2 pos, float grid_step) {
13 return ImVec2(std::floor(pos.x / grid_step) * grid_step,
14 std::floor(pos.y / grid_step) * grid_step);
15}
16
17float CalculateEffectiveScale(ImVec2 canvas_size, ImVec2 content_size,
18 float global_scale) {
19 if (content_size.x <= 0 || content_size.y <= 0)
20 return global_scale;
21
22 float scale_x = (canvas_size.x * global_scale) / content_size.x;
23 float scale_y = (canvas_size.y * global_scale) / content_size.y;
24 return std::min(scale_x, scale_y);
25}
26
27int GetTileIdFromPosition(ImVec2 mouse_pos, float tile_size, float scale,
28 int tiles_per_row) {
29 float scaled_tile_size = tile_size * scale;
30 int tile_x = static_cast<int>(mouse_pos.x / scaled_tile_size);
31 int tile_y = static_cast<int>(mouse_pos.y / scaled_tile_size);
32
33 return tile_x + (tile_y * tiles_per_row);
34}
35
36bool LoadROMPaletteGroups(Rom* rom, CanvasPaletteManager& palette_manager) {
37 if (!rom || palette_manager.palettes_loaded) {
38 return palette_manager.palettes_loaded;
39 }
40
41 try {
42 const auto& palette_groups = rom->palette_group();
43 palette_manager.rom_palette_groups.clear();
44 palette_manager.palette_group_names.clear();
45
46 // Overworld palettes
47 if (palette_groups.overworld_main.size() > 0) {
48 palette_manager.rom_palette_groups.push_back(
49 palette_groups.overworld_main[0]);
50 palette_manager.palette_group_names.push_back("Overworld Main");
51 }
52 if (palette_groups.overworld_aux.size() > 0) {
53 palette_manager.rom_palette_groups.push_back(
54 palette_groups.overworld_aux[0]);
55 palette_manager.palette_group_names.push_back("Overworld Aux");
56 }
57 if (palette_groups.overworld_animated.size() > 0) {
58 palette_manager.rom_palette_groups.push_back(
59 palette_groups.overworld_animated[0]);
60 palette_manager.palette_group_names.push_back("Overworld Animated");
61 }
62
63 // Dungeon palettes
64 if (palette_groups.dungeon_main.size() > 0) {
65 palette_manager.rom_palette_groups.push_back(
66 palette_groups.dungeon_main[0]);
67 palette_manager.palette_group_names.push_back("Dungeon Main");
68 }
69
70 // Sprite palettes
71 if (palette_groups.global_sprites.size() > 0) {
72 palette_manager.rom_palette_groups.push_back(
73 palette_groups.global_sprites[0]);
74 palette_manager.palette_group_names.push_back("Global Sprites");
75 }
76 if (palette_groups.armors.size() > 0) {
77 palette_manager.rom_palette_groups.push_back(palette_groups.armors[0]);
78 palette_manager.palette_group_names.push_back("Armor");
79 }
80 if (palette_groups.swords.size() > 0) {
81 palette_manager.rom_palette_groups.push_back(palette_groups.swords[0]);
82 palette_manager.palette_group_names.push_back("Swords");
83 }
84
85 palette_manager.palettes_loaded = true;
86 LOG_DEBUG("Canvas", "Loaded %zu ROM palette groups",
87 palette_manager.rom_palette_groups.size());
88 return true;
89
90 } catch (const std::exception& e) {
91 LOG_ERROR("Canvas", "Failed to load ROM palette groups");
92 return false;
93 }
94}
95
96bool ApplyPaletteGroup(gfx::IRenderer* renderer, gfx::Bitmap* bitmap, CanvasPaletteManager& palette_manager,
97 int group_index, int palette_index) {
98 if (!bitmap) return false;
99
100 if (group_index < 0 || group_index >= palette_manager.rom_palette_groups.size()) {
101 return false;
102 }
103
104 const auto& palette = palette_manager.rom_palette_groups[group_index];
105
106 // Apply the full palette or use SetPaletteWithTransparent if palette_index is specified
107 if (palette_index == 0) {
108 bitmap->SetPalette(palette);
109 } else {
110 bitmap->SetPaletteWithTransparent(palette, palette_index);
111 }
112 bitmap->set_modified(true);
113 palette_manager.palette_dirty = true;
114
115 // Queue texture update only if live_update is enabled
116 if (palette_manager.live_update_enabled && renderer) {
119 palette_manager.palette_dirty = false; // Clear dirty flag after update
120 }
121 return true;
122}
123
124// Drawing utility functions
125void DrawCanvasRect(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
126 int x, int y, int w, int h, ImVec4 color,
127 float global_scale) {
128 // Apply global scale to position and size
129 float scaled_x = x * global_scale;
130 float scaled_y = y * global_scale;
131 float scaled_w = w * global_scale;
132 float scaled_h = h * global_scale;
133
134 ImVec2 origin(canvas_p0.x + scrolling.x + scaled_x,
135 canvas_p0.y + scrolling.y + scaled_y);
136 ImVec2 size(canvas_p0.x + scrolling.x + scaled_x + scaled_w,
137 canvas_p0.y + scrolling.y + scaled_y + scaled_h);
138
139 uint32_t color_u32 = IM_COL32(color.x * 255, color.y * 255, color.z * 255, color.w * 255);
140 draw_list->AddRectFilled(origin, size, color_u32);
141
142 // Add a black outline
143 ImVec2 outline_origin(origin.x - 1, origin.y - 1);
144 ImVec2 outline_size(size.x + 1, size.y + 1);
145 draw_list->AddRect(outline_origin, outline_size, IM_COL32(0, 0, 0, 255));
146}
147
148void DrawCanvasText(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
149 const std::string& text, int x, int y, float global_scale) {
150 // Apply global scale to text position
151 float scaled_x = x * global_scale;
152 float scaled_y = y * global_scale;
153
154 ImVec2 text_pos(canvas_p0.x + scrolling.x + scaled_x,
155 canvas_p0.y + scrolling.y + scaled_y);
156
157 // Draw text with black shadow for better visibility
158 draw_list->AddText(ImVec2(text_pos.x + 1, text_pos.y + 1),
159 IM_COL32(0, 0, 0, 255), text.c_str());
160 draw_list->AddText(text_pos, IM_COL32(255, 255, 255, 255), text.c_str());
161}
162
163void DrawCanvasOutline(ImDrawList* draw_list, ImVec2 canvas_p0,
164 ImVec2 scrolling, int x, int y, int w, int h,
165 uint32_t color) {
166 ImVec2 origin(canvas_p0.x + scrolling.x + x, canvas_p0.y + scrolling.y + y);
167 ImVec2 size(canvas_p0.x + scrolling.x + x + w,
168 canvas_p0.y + scrolling.y + y + h);
169 draw_list->AddRect(origin, size, color, 0, 0, 1.5f);
170}
171
172void DrawCanvasOutlineWithColor(ImDrawList* draw_list, ImVec2 canvas_p0,
173 ImVec2 scrolling, int x, int y, int w, int h,
174 ImVec4 color) {
175 uint32_t color_u32 =
176 IM_COL32(color.x * 255, color.y * 255, color.z * 255, color.w * 255);
177 DrawCanvasOutline(draw_list, canvas_p0, scrolling, x, y, w, h, color_u32);
178}
179
180// Grid utility functions
181void DrawCanvasGridLines(ImDrawList* draw_list, ImVec2 canvas_p0,
182 ImVec2 canvas_p1, ImVec2 scrolling, float grid_step,
183 float global_scale) {
184 const uint32_t grid_color = IM_COL32(200, 200, 200, 50);
185 const float grid_thickness = 0.5f;
186
187 float scaled_grid_step = grid_step * global_scale;
188
189 for (float x = fmodf(scrolling.x, scaled_grid_step);
190 x < (canvas_p1.x - canvas_p0.x); x += scaled_grid_step) {
191 draw_list->AddLine(ImVec2(canvas_p0.x + x, canvas_p0.y),
192 ImVec2(canvas_p0.x + x, canvas_p1.y), grid_color,
193 grid_thickness);
194 }
195
196 for (float y = fmodf(scrolling.y, scaled_grid_step);
197 y < (canvas_p1.y - canvas_p0.y); y += scaled_grid_step) {
198 draw_list->AddLine(ImVec2(canvas_p0.x, canvas_p0.y + y),
199 ImVec2(canvas_p1.x, canvas_p0.y + y), grid_color,
200 grid_thickness);
201 }
202}
203
204void DrawCustomHighlight(ImDrawList* draw_list, ImVec2 canvas_p0,
205 ImVec2 scrolling, int highlight_tile_id,
206 float grid_step) {
207 if (highlight_tile_id == -1)
208 return;
209
210 int tile_x = highlight_tile_id % 8;
211 int tile_y = highlight_tile_id / 8;
212 ImVec2 tile_pos(canvas_p0.x + scrolling.x + tile_x * grid_step,
213 canvas_p0.y + scrolling.y + tile_y * grid_step);
214 ImVec2 tile_pos_end(tile_pos.x + grid_step, tile_pos.y + grid_step);
215
216 draw_list->AddRectFilled(tile_pos, tile_pos_end, IM_COL32(255, 0, 255, 255));
217}
218
219void DrawHexTileLabels(ImDrawList* draw_list, ImVec2 canvas_p0,
220 ImVec2 scrolling, ImVec2 canvas_sz, float grid_step,
221 float global_scale) {
222 float scaled_grid_step = grid_step * global_scale;
223
224 for (float x = fmodf(scrolling.x, scaled_grid_step);
225 x < canvas_sz.x * global_scale; x += scaled_grid_step) {
226 for (float y = fmodf(scrolling.y, scaled_grid_step);
227 y < canvas_sz.y * global_scale; y += scaled_grid_step) {
228 int tile_x = (x - scrolling.x) / scaled_grid_step;
229 int tile_y = (y - scrolling.y) / scaled_grid_step;
230 int tile_id = tile_x + (tile_y * 16);
231
232 char hex_id[8];
233 snprintf(hex_id, sizeof(hex_id), "%02X", tile_id);
234
235 draw_list->AddText(ImVec2(canvas_p0.x + x + (scaled_grid_step / 2) - 4,
236 canvas_p0.y + y + (scaled_grid_step / 2) - 4),
237 IM_COL32(255, 255, 255, 255), hex_id);
238 }
239 }
240}
241
242// Layout and interaction utilities
243ImVec2 CalculateCanvasSize(ImVec2 content_region, ImVec2 custom_size,
244 bool use_custom) {
245 return use_custom ? custom_size : content_region;
246}
247
248ImVec2 CalculateScaledCanvasSize(ImVec2 canvas_size, float global_scale) {
249 return ImVec2(canvas_size.x * global_scale, canvas_size.y * global_scale);
250}
251
252bool IsPointInCanvas(ImVec2 point, ImVec2 canvas_p0, ImVec2 canvas_p1) {
253 return point.x >= canvas_p0.x && point.x <= canvas_p1.x &&
254 point.y >= canvas_p0.y && point.y <= canvas_p1.y;
255}
256
257// Size reporting for ImGui table integration
258ImVec2 CalculateMinimumCanvasSize(ImVec2 content_size, float global_scale,
259 float padding) {
260 // Calculate minimum size needed to display content with padding
261 ImVec2 min_size = ImVec2(content_size.x * global_scale + padding * 2,
262 content_size.y * global_scale + padding * 2);
263
264 // Ensure minimum practical size
265 min_size.x = std::max(min_size.x, 64.0f);
266 min_size.y = std::max(min_size.y, 64.0f);
267
268 return min_size;
269}
270
271ImVec2 CalculatePreferredCanvasSize(ImVec2 content_size, float global_scale,
272 float min_scale) {
273 // Calculate preferred size with minimum scale constraint
274 float effective_scale = std::max(global_scale, min_scale);
275 ImVec2 preferred_size = ImVec2(content_size.x * effective_scale + 8.0f,
276 content_size.y * effective_scale + 8.0f);
277
278 // Cap to reasonable maximum sizes for table integration
279 preferred_size.x = std::min(preferred_size.x, 800.0f);
280 preferred_size.y = std::min(preferred_size.y, 600.0f);
281
282 return preferred_size;
283}
284
285void ReserveCanvasSpace(ImVec2 canvas_size, const std::string& label) {
286 // Reserve space in ImGui layout so tables know the size
287 if (!label.empty()) {
288 ImGui::Text("%s", label.c_str());
289 }
290 ImGui::Dummy(canvas_size);
291 ImGui::SameLine();
292 ImGui::SetCursorPosX(ImGui::GetCursorPosX() -
293 canvas_size.x); // Move back to start
294}
295
296void SetNextCanvasSize(ImVec2 size, bool auto_resize) {
297 if (auto_resize) {
298 // Use auto-sizing child window for table integration
299 ImGui::SetNextWindowContentSize(size);
300 } else {
301 // Fixed size
302 ImGui::SetNextWindowSize(size);
303 }
304}
305
306// High-level composite operations
307void DrawCanvasGrid(const CanvasRenderContext& ctx, int highlight_tile_id) {
308 if (!ctx.enable_grid)
309 return;
310
311 ctx.draw_list->PushClipRect(ctx.canvas_p0, ctx.canvas_p1, true);
312
313 // Draw grid lines
315 ctx.scrolling, ctx.grid_step, ctx.global_scale);
316
317 // Draw highlight if specified
318 if (highlight_tile_id != -1) {
320 highlight_tile_id, ctx.grid_step * ctx.global_scale);
321 }
322
323 // Draw hex labels if enabled
324 if (ctx.enable_hex_labels) {
326 ImVec2(ctx.canvas_p1.x - ctx.canvas_p0.x,
327 ctx.canvas_p1.y - ctx.canvas_p0.y),
328 ctx.grid_step, ctx.global_scale);
329 }
330
331 ctx.draw_list->PopClipRect();
332}
333
335 const ImVector<ImVec2>& points,
336 const ImVector<ImVec2>& selected_points) {
337 const ImVec2 origin(ctx.canvas_p0.x + ctx.scrolling.x,
338 ctx.canvas_p0.y + ctx.scrolling.y);
339
340 // Draw hover points
341 for (int n = 0; n < points.Size; n += 2) {
342 ctx.draw_list->AddRect(
343 ImVec2(origin.x + points[n].x, origin.y + points[n].y),
344 ImVec2(origin.x + points[n + 1].x, origin.y + points[n + 1].y),
345 IM_COL32(255, 255, 255, 255), 1.0f);
346 }
347
348 // Draw selection rectangles
349 if (!selected_points.empty()) {
350 for (int n = 0; n < selected_points.size(); n += 2) {
351 ctx.draw_list->AddRect(ImVec2(origin.x + selected_points[n].x,
352 origin.y + selected_points[n].y),
353 ImVec2(origin.x + selected_points[n + 1].x + 0x10,
354 origin.y + selected_points[n + 1].y + 0x10),
355 IM_COL32(255, 255, 255, 255), 1.0f);
356 }
357 }
358}
359
361 const ImVector<ImVector<std::string>>& labels,
362 int current_labels, int tile_id_offset) {
363 if (current_labels >= labels.size())
364 return;
365
366 float scaled_grid_step = ctx.grid_step * ctx.global_scale;
367
368 for (float x = fmodf(ctx.scrolling.x, scaled_grid_step);
369 x < (ctx.canvas_p1.x - ctx.canvas_p0.x); x += scaled_grid_step) {
370 for (float y = fmodf(ctx.scrolling.y, scaled_grid_step);
371 y < (ctx.canvas_p1.y - ctx.canvas_p0.y); y += scaled_grid_step) {
372 int tile_x = (x - ctx.scrolling.x) / scaled_grid_step;
373 int tile_y = (y - ctx.scrolling.y) / scaled_grid_step;
374 int tile_id = tile_x + (tile_y * tile_id_offset);
375
376 if (tile_id >= labels[current_labels].size()) {
377 break;
378 }
379
380 const std::string& label = labels[current_labels][tile_id];
381 ctx.draw_list->AddText(
382 ImVec2(ctx.canvas_p0.x + x + (scaled_grid_step / 2) - tile_id_offset,
383 ctx.canvas_p0.y + y + (scaled_grid_step / 2) - tile_id_offset),
384 IM_COL32(255, 255, 255, 255), label.c_str());
385 }
386 }
387}
388
389} // namespace CanvasUtils
390
391// CanvasConfig theme-aware methods implementation
393 if (!use_theme_sizing) {
394 return 32.0f; // Legacy fixed height
395 }
396
397 // Use layout helpers for theme-aware sizing
398 // We need to include layout_helpers.h in the implementation file
399 // For now, return a reasonable default that respects ImGui font size
400 return ImGui::GetFontSize() * 0.75f; // Will be replaced with LayoutHelpers call
401}
402
404 if (!use_theme_sizing) {
405 return grid_step; // Use configured grid_step as-is
406 }
407
408 // Apply minimal theme-aware adjustment based on font size
409 // Grid should stay consistent, but scale slightly with UI density
410 float base_spacing = ImGui::GetFontSize() * 0.5f;
411 return std::max(grid_step, base_spacing);
412}
413
414} // namespace gui
415} // namespace yaze
The Rom class is used to load, save, and modify Rom data.
Definition rom.h:74
auto palette_group() const
Definition rom.h:216
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
Definition arena.cc:32
static Arena & Get()
Definition arena.cc:15
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:66
void set_modified(bool modified)
Definition bitmap.h:296
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap.
Definition bitmap.cc:334
void SetPaletteWithTransparent(const SnesPalette &palette, size_t index, int length=7)
Set the palette with a transparent color.
Definition bitmap.cc:403
Defines an abstract interface for all rendering operations.
Definition irenderer.h:35
#define LOG_DEBUG(category, format,...)
Definition log.h:104
#define LOG_ERROR(category, format,...)
Definition log.h:110
void ReserveCanvasSpace(ImVec2 canvas_size, const std::string &label)
void SetNextCanvasSize(ImVec2 size, bool auto_resize)
void DrawCanvasRect(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, int x, int y, int w, int h, ImVec4 color, float global_scale)
void DrawCanvasLabels(const CanvasRenderContext &ctx, const ImVector< ImVector< std::string > > &labels, int current_labels, int tile_id_offset)
bool IsPointInCanvas(ImVec2 point, ImVec2 canvas_p0, ImVec2 canvas_p1)
void DrawCanvasOverlay(const CanvasRenderContext &ctx, const ImVector< ImVec2 > &points, const ImVector< ImVec2 > &selected_points)
ImVec2 CalculateCanvasSize(ImVec2 content_region, ImVec2 custom_size, bool use_custom)
bool LoadROMPaletteGroups(Rom *rom, CanvasPaletteManager &palette_manager)
ImVec2 CalculateMinimumCanvasSize(ImVec2 content_size, float global_scale, float padding)
float CalculateEffectiveScale(ImVec2 canvas_size, ImVec2 content_size, float global_scale)
void DrawCanvasOutline(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, int x, int y, int w, int h, uint32_t color)
int GetTileIdFromPosition(ImVec2 mouse_pos, float tile_size, float scale, int tiles_per_row)
void DrawCanvasOutlineWithColor(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, int x, int y, int w, int h, ImVec4 color)
bool ApplyPaletteGroup(gfx::IRenderer *renderer, gfx::Bitmap *bitmap, CanvasPaletteManager &palette_manager, int group_index, int palette_index)
void DrawCanvasGrid(const CanvasRenderContext &ctx, int highlight_tile_id)
ImVec2 CalculatePreferredCanvasSize(ImVec2 content_size, float global_scale, float min_scale)
void DrawCanvasText(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, const std::string &text, int x, int y, float global_scale)
ImVec2 CalculateScaledCanvasSize(ImVec2 canvas_size, float global_scale)
void DrawCustomHighlight(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, int highlight_tile_id, float grid_step)
void DrawCanvasGridLines(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 canvas_p1, ImVec2 scrolling, float grid_step, float global_scale)
ImVec2 AlignToGrid(ImVec2 pos, float grid_step)
void DrawHexTileLabels(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, ImVec2 canvas_sz, float grid_step, float global_scale)
Main namespace for the application.
Definition controller.cc:20
float GetGridSpacing() const
float GetToolbarHeight() const
Palette management state for canvas.
std::vector< gfx::SnesPalette > rom_palette_groups
std::vector< std::string > palette_group_names