yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
canvas_runtime_draw.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cmath>
5
12#include "imgui/imgui.h"
13
14namespace yaze::gui {
15
16namespace {
18 CanvasGeometry geom;
19 geom.canvas_p0 = rt.canvas_p0;
20 geom.canvas_sz = rt.canvas_sz;
21 geom.scrolling = rt.scrolling;
22 geom.scaled_size =
23 ImVec2(rt.canvas_sz.x * rt.scale, rt.canvas_sz.y * rt.scale);
24 geom.canvas_p1 = ImVec2(geom.canvas_p0.x + geom.canvas_sz.x,
25 geom.canvas_p0.y + geom.canvas_sz.y);
26 return geom;
27}
28
29ImVec2 AlignPosToGridHelper(ImVec2 pos, float scale) {
30 return ImVec2(std::floor(pos.x / scale) * scale,
31 std::floor(pos.y / scale) * scale);
32}
33} // namespace
34
35void DrawBitmap(const CanvasRuntime& rt, gfx::Bitmap& bitmap, int border_offset,
36 float scale) {
37 if (!rt.draw_list)
38 return;
39 CanvasGeometry geom = GetGeometryFromRuntime(rt);
40 RenderBitmapOnCanvas(rt.draw_list, geom, bitmap, border_offset, scale);
41}
42
43void DrawBitmap(const CanvasRuntime& rt, gfx::Bitmap& bitmap, int x_offset,
44 int y_offset, float scale, int alpha) {
45 if (!rt.draw_list)
46 return;
47 CanvasGeometry geom = GetGeometryFromRuntime(rt);
48 RenderBitmapOnCanvas(rt.draw_list, geom, bitmap, x_offset, y_offset, scale,
49 alpha);
50}
51
52void DrawBitmap(const CanvasRuntime& rt, gfx::Bitmap& bitmap, ImVec2 dest_pos,
53 ImVec2 dest_size, ImVec2 src_pos, ImVec2 src_size) {
54 if (!rt.draw_list)
55 return;
56 CanvasGeometry geom = GetGeometryFromRuntime(rt);
57 RenderBitmapOnCanvas(rt.draw_list, geom, bitmap, dest_pos, dest_size, src_pos,
58 src_size);
59}
60
61void DrawBitmap(const CanvasRuntime& rt, gfx::Bitmap& bitmap,
62 const BitmapDrawOpts& opts) {
63 if (!rt.draw_list)
64 return;
65
66 if (opts.ensure_texture && !bitmap.texture() && bitmap.surface()) {
69 }
70
71 if (opts.dest_size.x > 0 && opts.dest_size.y > 0) {
72 ImVec2 src_size = opts.src_size;
73 if (src_size.x <= 0 || src_size.y <= 0) {
74 src_size = ImVec2(static_cast<float>(bitmap.width()),
75 static_cast<float>(bitmap.height()));
76 }
77 DrawBitmap(rt, bitmap, opts.dest_pos, opts.dest_size, opts.src_pos,
78 src_size);
79 } else {
80 DrawBitmap(rt, bitmap, static_cast<int>(opts.dest_pos.x),
81 static_cast<int>(opts.dest_pos.y), opts.scale, opts.alpha);
82 }
83}
84
86 const BitmapPreviewOptions& options) {
87 if (options.ensure_texture && !bitmap.texture() && bitmap.surface()) {
90 }
91
92 if (options.dest_size.x > 0 && options.dest_size.y > 0) {
93 ImVec2 src_size = options.src_size;
94 if (src_size.x <= 0 || src_size.y <= 0) {
95 src_size = ImVec2(bitmap.width(), bitmap.height());
96 }
97 DrawBitmap(rt, bitmap, options.dest_pos, options.dest_size, options.src_pos,
98 src_size);
99 } else {
100 DrawBitmap(rt, bitmap, static_cast<int>(options.dest_pos.x),
101 static_cast<int>(options.dest_pos.y), options.scale,
102 options.alpha);
103 }
104}
105
107 const PreviewPanelOpts& opts) {
108 if (!rt.draw_list)
109 return false;
110
111 if (opts.ensure_texture && !bmp.texture() && bmp.surface()) {
114 }
115
116 if (opts.dest_size.x > 0 && opts.dest_size.y > 0) {
117 DrawBitmap(rt, bmp, opts.dest_pos, opts.dest_size, ImVec2(0, 0),
118 ImVec2(static_cast<float>(bmp.width()),
119 static_cast<float>(bmp.height())));
120 } else {
121 DrawBitmap(rt, bmp, static_cast<int>(opts.dest_pos.x),
122 static_cast<int>(opts.dest_pos.y), 1.0f, 255);
123 }
124 return true;
125}
126
127void DrawRect(const CanvasRuntime& rt, int x, int y, int w, int h,
128 ImVec4 color) {
129 if (!rt.draw_list)
130 return;
132 h, color, rt.scale);
133}
134
135void DrawText(const CanvasRuntime& rt, const std::string& text, int x, int y) {
136 if (!rt.draw_list)
137 return;
139 y, rt.scale);
140}
141
142void DrawOutline(const CanvasRuntime& rt, int x, int y, int w, int h,
143 ImU32 color) {
144 if (!rt.draw_list)
145 return;
147 w, h, color, rt.scale);
148}
149
151 int current_tile, ImVec2* out_drawn_pos) {
152 if (!rt.draw_list)
153 return false;
154
155 const ImGuiIO& io = ImGui::GetIO();
156 const ImVec2 origin(rt.canvas_p0.x + rt.scrolling.x,
157 rt.canvas_p0.y + rt.scrolling.y);
158 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
159
160 if (!tilemap.atlas.is_active() || tilemap.tile_size.x <= 0) {
161 return false;
162 }
163
164 const float scaled_size = tilemap.tile_size.x * rt.scale;
165
166 if (!rt.hovered) {
167 return false;
168 }
169
170 ImVec2 paint_pos = AlignPosToGridHelper(mouse_pos, scaled_size);
171
172 if (tilemap.atlas.is_active() && tilemap.atlas.texture()) {
173 int tiles_per_row = tilemap.atlas.width() / tilemap.tile_size.x;
174 if (tiles_per_row > 0) {
175 int tile_x = (current_tile % tiles_per_row) * tilemap.tile_size.x;
176 int tile_y = (current_tile / tiles_per_row) * tilemap.tile_size.y;
177
178 if (tile_x >= 0 && tile_x < tilemap.atlas.width() && tile_y >= 0 &&
179 tile_y < tilemap.atlas.height()) {
180 ImVec2 uv0 =
181 ImVec2(static_cast<float>(tile_x) / tilemap.atlas.width(),
182 static_cast<float>(tile_y) / tilemap.atlas.height());
183 ImVec2 uv1 = ImVec2(static_cast<float>(tile_x + tilemap.tile_size.x) /
184 tilemap.atlas.width(),
185 static_cast<float>(tile_y + tilemap.tile_size.y) /
186 tilemap.atlas.height());
187
188 ScopedNearestSampling nearest_sampling(rt.draw_list);
189 rt.draw_list->AddImage(
190 (ImTextureID)(intptr_t)tilemap.atlas.texture(),
191 ImVec2(origin.x + paint_pos.x, origin.y + paint_pos.y),
192 ImVec2(origin.x + paint_pos.x + scaled_size,
193 origin.y + paint_pos.y + scaled_size),
194 uv0, uv1, ImGui::GetColorU32(ImVec4(1, 1, 1, 1)));
195 }
196 }
197 }
198
199 if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) ||
200 ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
201 if (out_drawn_pos)
202 *out_drawn_pos = paint_pos;
203 return true;
204 }
205
206 return false;
207}
208
209bool DrawTileSelector(const CanvasRuntime& rt, int size, int size_y,
210 ImVec2* out_selected_pos) {
211 const ImGuiIO& io = ImGui::GetIO();
212 const ImVec2 origin(rt.canvas_p0.x + rt.scrolling.x,
213 rt.canvas_p0.y + rt.scrolling.y);
214 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
215
216 if (size_y == 0) {
217 size_y = size;
218 }
219
220 if (rt.hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
221 ImVec2 painter_pos =
222 AlignPosToGridHelper(mouse_pos, static_cast<float>(size));
223 if (out_selected_pos)
224 *out_selected_pos = painter_pos;
225 }
226
227 if (rt.hovered && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
228 return true;
229 }
230
231 return false;
232}
233
234void DrawSelectRect(const CanvasRuntime& rt, int current_map, int tile_size,
235 float scale, CanvasSelection& selection) {
236 if (!rt.draw_list)
237 return;
238
239 const ImGuiIO& io = ImGui::GetIO();
240 const ImVec2 origin(rt.canvas_p0.x + rt.scrolling.x,
241 rt.canvas_p0.y + rt.scrolling.y);
242 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
243 static ImVec2 drag_start_pos;
244 const float scaled_size = tile_size * scale;
245 static bool dragging = false;
246 constexpr int small_map_size = 0x200;
247 constexpr uint32_t kWhite = IM_COL32(255, 255, 255, 255);
248
249 if (!rt.hovered) {
250 return;
251 }
252
253 int superY, superX;
254 if (current_map < 0x40) {
255 superY = current_map / 8;
256 superX = current_map % 8;
257 } else if (current_map < 0x80) {
258 superY = (current_map - 0x40) / 8;
259 superX = (current_map - 0x40) % 8;
260 } else {
261 superY = (current_map - 0x80) / 8;
262 superX = (current_map - 0x80) % 8;
263 }
264
265 if (ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
266 ImVec2 painter_pos = AlignPosToGridHelper(mouse_pos, scaled_size);
267 int world_x = static_cast<int>(painter_pos.x / scale);
268 int world_y = static_cast<int>(painter_pos.y / scale);
269
270 auto tile16_x = (world_x % small_map_size) / (small_map_size / 0x20);
271 auto tile16_y = (world_y % small_map_size) / (small_map_size / 0x20);
272
273 int index_x = superX * 0x20 + tile16_x;
274 int index_y = superY * 0x20 + tile16_y;
275 selection.selected_tile_pos =
276 ImVec2(static_cast<float>(index_x), static_cast<float>(index_y));
277 selection.selected_points.clear();
278 selection.select_rect_active = false;
279
280 drag_start_pos = AlignPosToGridHelper(mouse_pos, scaled_size);
281 }
282
283 ImVec2 drag_end_pos = AlignPosToGridHelper(mouse_pos, scaled_size);
284 if (ImGui::IsMouseDragging(ImGuiMouseButton_Right)) {
285 // Order the tile corners before adding the tile size: dragging up or left
286 // otherwise inverts the rect, and ImGui 1.93 draws nothing for it.
287 const float min_x = std::min(drag_start_pos.x, drag_end_pos.x);
288 const float min_y = std::min(drag_start_pos.y, drag_end_pos.y);
289 const float max_x = std::max(drag_start_pos.x, drag_end_pos.x);
290 const float max_y = std::max(drag_start_pos.y, drag_end_pos.y);
291 auto start = ImVec2(origin.x + min_x, origin.y + min_y);
292 auto end =
293 ImVec2(origin.x + max_x + scaled_size, origin.y + max_y + scaled_size);
294 rt.draw_list->AddRect(start, end, kWhite);
295 dragging = true;
296 }
297
298 if (dragging && !ImGui::IsMouseDown(ImGuiMouseButton_Right)) {
299 dragging = false;
300
301 constexpr int tile16_size = 16;
302 int start_x = static_cast<int>(std::floor(drag_start_pos.x / scaled_size)) *
303 tile16_size;
304 int start_y = static_cast<int>(std::floor(drag_start_pos.y / scaled_size)) *
305 tile16_size;
306 int end_x = static_cast<int>(std::floor(drag_end_pos.x / scaled_size)) *
307 tile16_size;
308 int end_y = static_cast<int>(std::floor(drag_end_pos.y / scaled_size)) *
309 tile16_size;
310
311 if (start_x > end_x)
312 std::swap(start_x, end_x);
313 if (start_y > end_y)
314 std::swap(start_y, end_y);
315
316 selection.selected_tiles.clear();
317 selection.selected_tiles.reserve(
318 static_cast<size_t>(((end_x - start_x) / tile16_size + 1) *
319 ((end_y - start_y) / tile16_size + 1)));
320
321 constexpr int tiles_per_local_map = small_map_size / 16;
322
323 for (int y = start_y; y <= end_y; y += tile16_size) {
324 for (int x = start_x; x <= end_x; x += tile16_size) {
325 int local_map_x = (x / small_map_size) % 8;
326 int local_map_y = (y / small_map_size) % 8;
327 int tile16_x = (x % small_map_size) / tile16_size;
328 int tile16_y = (y % small_map_size) / tile16_size;
329 int index_x = local_map_x * tiles_per_local_map + tile16_x;
330 int index_y = local_map_y * tiles_per_local_map + tile16_y;
331 selection.selected_tiles.emplace_back(static_cast<float>(index_x),
332 static_cast<float>(index_y));
333 }
334 }
335
336 selection.selected_points.clear();
337 selection.selected_points.push_back(
338 ImVec2(drag_start_pos.x / scale, drag_start_pos.y / scale));
339 selection.selected_points.push_back(
340 ImVec2(drag_end_pos.x / scale, drag_end_pos.y / scale));
341 selection.select_rect_active = true;
342 }
343}
344
345} // namespace yaze::gui
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
Definition arena.cc:39
static Arena & Get()
Definition arena.cc:24
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:70
TextureHandle texture() const
Definition bitmap.h:410
bool is_active() const
Definition bitmap.h:414
int height() const
Definition bitmap.h:399
int width() const
Definition bitmap.h:398
SDL_Surface * surface() const
Definition bitmap.h:409
RAII scope that samples the images drawn inside it with nearest-neighbor filtering.
void DrawCanvasRect(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, int x, int y, int w, int h, ImVec4 color, float global_scale)
void DrawCanvasOutline(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, int x, int y, int w, int h, uint32_t color, float global_scale)
void DrawCanvasText(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, const std::string &text, int x, int y, float global_scale)
CanvasGeometry GetGeometryFromRuntime(const CanvasRuntime &rt)
Graphical User Interface (GUI) components for the application.
void DrawBitmapPreview(const CanvasRuntime &rt, gfx::Bitmap &bitmap, const BitmapPreviewOptions &options)
bool DrawTileSelector(const CanvasRuntime &rt, int size, int size_y, ImVec2 *out_selected_pos)
bool DrawTilemapPainter(const CanvasRuntime &rt, gfx::Tilemap &tilemap, int current_tile, ImVec2 *out_drawn_pos)
void DrawRect(const CanvasRuntime &rt, int x, int y, int w, int h, ImVec4 color)
void DrawBitmap(const CanvasRuntime &rt, gfx::Bitmap &bitmap, int border_offset=2, float scale=1.0f)
void DrawOutline(const CanvasRuntime &rt, int x, int y, int w, int h, ImU32 color=IM_COL32(255, 255, 255, 200))
void DrawText(const CanvasRuntime &rt, const std::string &text, int x, int y)
void RenderBitmapOnCanvas(ImDrawList *draw_list, const CanvasGeometry &geometry, gfx::Bitmap &bitmap, int, float scale)
Render bitmap on canvas (border offset variant)
void DrawSelectRect(const CanvasRuntime &rt, int current_map, int tile_size, float scale, CanvasSelection &selection)
bool RenderPreviewPanel(const CanvasRuntime &rt, gfx::Bitmap &bmp, const PreviewPanelOpts &opts)
int y
Y coordinate or height.
Definition tilemap.h:21
int x
X coordinate or width.
Definition tilemap.h:20
Tilemap structure for SNES tile-based graphics management.
Definition tilemap.h:118
Pair tile_size
Size of individual tiles (8x8 or 16x16)
Definition tilemap.h:123
Bitmap atlas
Master bitmap containing all tiles.
Definition tilemap.h:119
Canvas geometry calculated per-frame.
Selection state for canvas interactions.
std::vector< ImVec2 > selected_tiles
std::vector< ImVec2 > selected_points