feat(canvas): introduce canvas geometry and rendering helpers
- Added new files `canvas_geometry.cc`, `canvas_geometry.h`, `canvas_rendering.cc`, and `canvas_rendering.h` to encapsulate canvas geometry calculations and rendering logic. - Refactored `Canvas::DrawBackground` and related methods to utilize the new geometry and rendering helpers, improving code organization and readability. - Introduced `CanvasState` to consolidate canvas state management, gradually replacing scattered state members. Benefits: - Enhances maintainability and clarity of canvas-related code. - Streamlines rendering operations and geometry calculations, improving overall performance and usability.
This commit is contained in:
@@ -371,60 +371,56 @@ void Canvas::UpdateInfoGrid(ImVec2 bg_size, float grid_size, int label_id) {
|
||||
|
||||
void Canvas::DrawBackground(ImVec2 canvas_size) {
|
||||
draw_list_ = GetWindowDrawList();
|
||||
canvas_p0_ = GetCursorScreenPos();
|
||||
|
||||
// Calculate canvas size using utility function
|
||||
ImVec2 content_region = GetContentRegionAvail();
|
||||
canvas_sz_ = CanvasUtils::CalculateCanvasSize(
|
||||
content_region, config_.canvas_size, config_.custom_canvas_size);
|
||||
|
||||
|
||||
// Phase 1: Calculate geometry using new helper
|
||||
state_.geometry = CalculateCanvasGeometry(
|
||||
config_, canvas_size,
|
||||
GetCursorScreenPos(),
|
||||
GetContentRegionAvail());
|
||||
|
||||
// Sync legacy fields for backward compatibility
|
||||
canvas_p0_ = state_.geometry.canvas_p0;
|
||||
canvas_p1_ = state_.geometry.canvas_p1;
|
||||
canvas_sz_ = state_.geometry.canvas_sz;
|
||||
scrolling_ = state_.geometry.scrolling;
|
||||
|
||||
// Update config if explicit size provided
|
||||
if (canvas_size.x != 0) {
|
||||
canvas_sz_ = canvas_size;
|
||||
config_.canvas_size = canvas_size;
|
||||
}
|
||||
|
||||
// Phase 1: Render background using helper
|
||||
RenderCanvasBackground(draw_list_, state_.geometry);
|
||||
|
||||
// Calculate scaled canvas bounds
|
||||
ImVec2 scaled_size =
|
||||
CanvasUtils::CalculateScaledCanvasSize(canvas_sz_, config_.global_scale);
|
||||
|
||||
// CRITICAL FIX: Ensure minimum size to prevent ImGui assertions
|
||||
if (scaled_size.x <= 0.0f)
|
||||
scaled_size.x = 1.0f;
|
||||
if (scaled_size.y <= 0.0f)
|
||||
scaled_size.y = 1.0f;
|
||||
|
||||
canvas_p1_ =
|
||||
ImVec2(canvas_p0_.x + scaled_size.x, canvas_p0_.y + scaled_size.y);
|
||||
|
||||
// Draw border and background color
|
||||
draw_list_->AddRectFilled(canvas_p0_, canvas_p1_, kRectangleColor);
|
||||
draw_list_->AddRect(canvas_p0_, canvas_p1_, kWhiteColor);
|
||||
|
||||
ImGui::InvisibleButton(canvas_id_.c_str(), scaled_size, kMouseFlags);
|
||||
ImGui::InvisibleButton(canvas_id_.c_str(), state_.geometry.scaled_size, kMouseFlags);
|
||||
|
||||
// CRITICAL FIX: Always update hover mouse position when hovering over canvas
|
||||
// This fixes the regression where CheckForCurrentMap() couldn't track hover
|
||||
// Phase 1: Use geometry helper for mouse calculation
|
||||
if (IsItemHovered()) {
|
||||
const ImGuiIO& io = GetIO();
|
||||
const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
|
||||
const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
|
||||
mouse_pos_in_canvas_ = mouse_pos;
|
||||
mouse_pos_in_canvas_ = CalculateMouseInCanvas(state_.geometry, io.MousePos);
|
||||
state_.mouse_pos_in_canvas = mouse_pos_in_canvas_; // Sync to state
|
||||
state_.is_hovered = true;
|
||||
is_hovered_ = true;
|
||||
} else {
|
||||
state_.is_hovered = false;
|
||||
is_hovered_ = false;
|
||||
}
|
||||
|
||||
// Pan handling (Phase 1: Use geometry helper)
|
||||
if (config_.is_draggable && IsItemHovered()) {
|
||||
const ImGuiIO& io = GetIO();
|
||||
const bool is_active = IsItemActive(); // Held
|
||||
const ImVec2 origin(canvas_p0_.x + scrolling_.x,
|
||||
canvas_p0_.y + scrolling_.y); // Lock scrolled origin
|
||||
const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
|
||||
|
||||
// Pan (we use a zero mouse threshold when there's no context menu)
|
||||
if (const float mouse_threshold_for_pan =
|
||||
enable_context_menu_ ? -1.0f : 0.0f;
|
||||
is_active &&
|
||||
IsMouseDragging(ImGuiMouseButton_Right, mouse_threshold_for_pan)) {
|
||||
scrolling_.x += io.MouseDelta.x;
|
||||
scrolling_.y += io.MouseDelta.y;
|
||||
ApplyScrollDelta(state_.geometry, io.MouseDelta);
|
||||
scrolling_ = state_.geometry.scrolling; // Sync legacy field
|
||||
config_.scrolling = scrolling_; // Sync config
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1050,7 +1046,7 @@ void Canvas::DrawSelectRect(int current_map, int tile_size, float scale) {
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::DrawBitmap(Bitmap& bitmap, int /*border_offset*/, float scale) {
|
||||
void Canvas::DrawBitmap(Bitmap& bitmap, int border_offset, float scale) {
|
||||
if (!bitmap.is_active()) {
|
||||
return;
|
||||
}
|
||||
@@ -1059,11 +1055,8 @@ void Canvas::DrawBitmap(Bitmap& bitmap, int /*border_offset*/, float scale) {
|
||||
// Update content size for table integration
|
||||
config_.content_size = ImVec2(bitmap.width(), bitmap.height());
|
||||
|
||||
draw_list_->AddImage((ImTextureID)(intptr_t)bitmap.texture(),
|
||||
ImVec2(canvas_p0_.x, canvas_p0_.y),
|
||||
ImVec2(canvas_p0_.x + (bitmap.width() * scale),
|
||||
canvas_p0_.y + (bitmap.height() * scale)));
|
||||
draw_list_->AddRect(canvas_p0_, canvas_p1_, kWhiteColor);
|
||||
// Phase 1: Use rendering helper
|
||||
RenderBitmapOnCanvas(draw_list_, state_.geometry, bitmap, border_offset, scale);
|
||||
}
|
||||
|
||||
void Canvas::DrawBitmap(Bitmap& bitmap, int x_offset, int y_offset, float scale,
|
||||
@@ -1077,23 +1070,8 @@ void Canvas::DrawBitmap(Bitmap& bitmap, int x_offset, int y_offset, float scale,
|
||||
// CRITICAL: Store UNSCALED bitmap size as content - scale is applied during rendering
|
||||
config_.content_size = ImVec2(bitmap.width(), bitmap.height());
|
||||
|
||||
// Calculate the actual rendered size including scale and offsets
|
||||
// CRITICAL: Use scale parameter (NOT global_scale_) for per-bitmap scaling
|
||||
ImVec2 rendered_size(bitmap.width() * scale, bitmap.height() * scale);
|
||||
ImVec2 total_size(x_offset + rendered_size.x, y_offset + rendered_size.y);
|
||||
|
||||
// CRITICAL FIX: Draw bitmap WITHOUT additional global_scale multiplication
|
||||
// The scale parameter already contains the correct scale factor
|
||||
// The scrolling should NOT be scaled - it's already in screen space
|
||||
draw_list_->AddImage(
|
||||
(ImTextureID)(intptr_t)bitmap.texture(),
|
||||
ImVec2(canvas_p0_.x + x_offset + scrolling_.x,
|
||||
canvas_p0_.y + y_offset + scrolling_.y),
|
||||
ImVec2(canvas_p0_.x + x_offset + scrolling_.x + rendered_size.x,
|
||||
canvas_p0_.y + y_offset + scrolling_.y + rendered_size.y),
|
||||
ImVec2(0, 0), ImVec2(1, 1), IM_COL32(255, 255, 255, alpha));
|
||||
|
||||
// Note: Content size for child windows should be set before BeginChild, not here
|
||||
// Phase 1: Use rendering helper
|
||||
RenderBitmapOnCanvas(draw_list_, state_.geometry, bitmap, x_offset, y_offset, scale, alpha);
|
||||
}
|
||||
|
||||
void Canvas::DrawBitmap(Bitmap& bitmap, ImVec2 dest_pos, ImVec2 dest_size,
|
||||
@@ -1106,14 +1084,8 @@ void Canvas::DrawBitmap(Bitmap& bitmap, ImVec2 dest_pos, ImVec2 dest_size,
|
||||
// Update content size for table integration
|
||||
config_.content_size = ImVec2(bitmap.width(), bitmap.height());
|
||||
|
||||
draw_list_->AddImage(
|
||||
(ImTextureID)(intptr_t)bitmap.texture(),
|
||||
ImVec2(canvas_p0_.x + dest_pos.x, canvas_p0_.y + dest_pos.y),
|
||||
ImVec2(canvas_p0_.x + dest_pos.x + dest_size.x,
|
||||
canvas_p0_.y + dest_pos.y + dest_size.y),
|
||||
ImVec2(src_pos.x / bitmap.width(), src_pos.y / bitmap.height()),
|
||||
ImVec2((src_pos.x + src_size.x) / bitmap.width(),
|
||||
(src_pos.y + src_size.y) / bitmap.height()));
|
||||
// Phase 1: Use rendering helper
|
||||
RenderBitmapOnCanvas(draw_list_, state_.geometry, bitmap, dest_pos, dest_size, src_pos, src_size);
|
||||
}
|
||||
|
||||
// TODO: Add parameters for sizing and positioning
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
#include "app/gfx/core/bitmap.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/gui/canvas/canvas_utils.h"
|
||||
#include "app/gui/canvas/canvas_state.h"
|
||||
#include "app/gui/canvas/canvas_geometry.h"
|
||||
#include "app/gui/canvas/canvas_rendering.h"
|
||||
#include "app/gui/widgets/palette_editor_widget.h"
|
||||
#include "app/gfx/util/bpp_format_manager.h"
|
||||
#include "app/gui/canvas/bpp_format_ui.h"
|
||||
@@ -414,6 +417,9 @@ class Canvas {
|
||||
CanvasSelection selection_;
|
||||
std::unique_ptr<PaletteEditorWidget> palette_editor_;
|
||||
|
||||
// Phase 1: Consolidated state (gradually replacing scattered members)
|
||||
CanvasState state_;
|
||||
|
||||
// Automation API (lazy-initialized on first access)
|
||||
std::unique_ptr<CanvasAutomationAPI> automation_api_;
|
||||
|
||||
|
||||
84
src/app/gui/canvas/canvas_geometry.cc
Normal file
84
src/app/gui/canvas/canvas_geometry.cc
Normal file
@@ -0,0 +1,84 @@
|
||||
#include "canvas_geometry.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include "app/gui/canvas/canvas_utils.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
CanvasGeometry CalculateCanvasGeometry(
|
||||
const CanvasConfig& config,
|
||||
ImVec2 requested_size,
|
||||
ImVec2 cursor_screen_pos,
|
||||
ImVec2 content_region_avail) {
|
||||
|
||||
CanvasGeometry geometry;
|
||||
|
||||
// Set canvas top-left position (screen space)
|
||||
geometry.canvas_p0 = cursor_screen_pos;
|
||||
|
||||
// Calculate canvas size using existing utility function
|
||||
ImVec2 canvas_sz = CanvasUtils::CalculateCanvasSize(
|
||||
content_region_avail, config.canvas_size, config.custom_canvas_size);
|
||||
|
||||
// Override with explicit size if provided
|
||||
if (requested_size.x != 0) {
|
||||
canvas_sz = requested_size;
|
||||
}
|
||||
|
||||
geometry.canvas_sz = canvas_sz;
|
||||
|
||||
// Calculate scaled canvas bounds
|
||||
geometry.scaled_size = CanvasUtils::CalculateScaledCanvasSize(
|
||||
canvas_sz, config.global_scale);
|
||||
|
||||
// CRITICAL: Ensure minimum size to prevent ImGui assertions
|
||||
if (geometry.scaled_size.x <= 0.0f) {
|
||||
geometry.scaled_size.x = 1.0f;
|
||||
}
|
||||
if (geometry.scaled_size.y <= 0.0f) {
|
||||
geometry.scaled_size.y = 1.0f;
|
||||
}
|
||||
|
||||
// Calculate bottom-right position
|
||||
geometry.canvas_p1 = ImVec2(
|
||||
geometry.canvas_p0.x + geometry.scaled_size.x,
|
||||
geometry.canvas_p0.y + geometry.scaled_size.y);
|
||||
|
||||
// Copy scroll offset from config (will be updated by interaction)
|
||||
geometry.scrolling = config.scrolling;
|
||||
|
||||
return geometry;
|
||||
}
|
||||
|
||||
ImVec2 CalculateMouseInCanvas(
|
||||
const CanvasGeometry& geometry,
|
||||
ImVec2 mouse_screen_pos) {
|
||||
|
||||
// Calculate origin (locked scrolled origin as used throughout canvas.cc)
|
||||
ImVec2 origin = GetCanvasOrigin(geometry);
|
||||
|
||||
// Convert screen space to canvas space
|
||||
return ImVec2(
|
||||
mouse_screen_pos.x - origin.x,
|
||||
mouse_screen_pos.y - origin.y);
|
||||
}
|
||||
|
||||
bool IsPointInCanvasBounds(
|
||||
const CanvasGeometry& geometry,
|
||||
ImVec2 point) {
|
||||
|
||||
return point.x >= geometry.canvas_p0.x &&
|
||||
point.x <= geometry.canvas_p1.x &&
|
||||
point.y >= geometry.canvas_p0.y &&
|
||||
point.y <= geometry.canvas_p1.y;
|
||||
}
|
||||
|
||||
void ApplyScrollDelta(CanvasGeometry& geometry, ImVec2 delta) {
|
||||
geometry.scrolling.x += delta.x;
|
||||
geometry.scrolling.y += delta.y;
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
87
src/app/gui/canvas/canvas_geometry.h
Normal file
87
src/app/gui/canvas/canvas_geometry.h
Normal file
@@ -0,0 +1,87 @@
|
||||
#ifndef YAZE_APP_GUI_CANVAS_CANVAS_GEOMETRY_H
|
||||
#define YAZE_APP_GUI_CANVAS_CANVAS_GEOMETRY_H
|
||||
|
||||
#include "app/gui/canvas/canvas_state.h"
|
||||
#include "app/gui/canvas/canvas_utils.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
/**
|
||||
* @brief Calculate canvas geometry from configuration and ImGui context
|
||||
*
|
||||
* Extracts the geometry calculation logic from Canvas::DrawBackground().
|
||||
* Computes screen-space positions, sizes, and scroll offsets for a canvas
|
||||
* based on its configuration and the current ImGui layout state.
|
||||
*
|
||||
* @param config Canvas configuration (size, scale, custom size flag)
|
||||
* @param requested_size Explicitly requested canvas size (0,0 = use config)
|
||||
* @param cursor_screen_pos Current ImGui cursor position (from GetCursorScreenPos)
|
||||
* @param content_region_avail Available content region (from GetContentRegionAvail)
|
||||
* @return Calculated geometry for this frame
|
||||
*/
|
||||
CanvasGeometry CalculateCanvasGeometry(
|
||||
const CanvasConfig& config,
|
||||
ImVec2 requested_size,
|
||||
ImVec2 cursor_screen_pos,
|
||||
ImVec2 content_region_avail);
|
||||
|
||||
/**
|
||||
* @brief Calculate mouse position in canvas space
|
||||
*
|
||||
* Converts screen-space mouse coordinates to canvas-space coordinates,
|
||||
* accounting for canvas position and scroll offset. This is the correct
|
||||
* coordinate system for tile/entity placement calculations.
|
||||
*
|
||||
* @param geometry Canvas geometry (must be current frame)
|
||||
* @param mouse_screen_pos Mouse position in screen space
|
||||
* @return Mouse position in canvas space
|
||||
*/
|
||||
ImVec2 CalculateMouseInCanvas(
|
||||
const CanvasGeometry& geometry,
|
||||
ImVec2 mouse_screen_pos);
|
||||
|
||||
/**
|
||||
* @brief Check if a point is within canvas bounds
|
||||
*
|
||||
* Tests whether a screen-space point lies within the canvas rectangle.
|
||||
* Useful for hit testing and hover detection.
|
||||
*
|
||||
* @param geometry Canvas geometry (must be current frame)
|
||||
* @param point Point in screen space to test
|
||||
* @return True if point is within canvas bounds
|
||||
*/
|
||||
bool IsPointInCanvasBounds(
|
||||
const CanvasGeometry& geometry,
|
||||
ImVec2 point);
|
||||
|
||||
/**
|
||||
* @brief Apply scroll delta to geometry
|
||||
*
|
||||
* Updates the scroll offset in the geometry. Used for pan operations.
|
||||
*
|
||||
* @param geometry Canvas geometry to update
|
||||
* @param delta Scroll delta (typically ImGui::GetIO().MouseDelta)
|
||||
*/
|
||||
void ApplyScrollDelta(CanvasGeometry& geometry, ImVec2 delta);
|
||||
|
||||
/**
|
||||
* @brief Get origin point (canvas top-left + scroll offset)
|
||||
*
|
||||
* Computes the "locked scrolled origin" used throughout canvas rendering.
|
||||
* This is the reference point for all canvas-space to screen-space conversions.
|
||||
*
|
||||
* @param geometry Canvas geometry
|
||||
* @return Origin point in screen space
|
||||
*/
|
||||
inline ImVec2 GetCanvasOrigin(const CanvasGeometry& geometry) {
|
||||
return ImVec2(geometry.canvas_p0.x + geometry.scrolling.x,
|
||||
geometry.canvas_p0.y + geometry.scrolling.y);
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_GUI_CANVAS_CANVAS_GEOMETRY_H
|
||||
|
||||
261
src/app/gui/canvas/canvas_rendering.cc
Normal file
261
src/app/gui/canvas/canvas_rendering.cc
Normal file
@@ -0,0 +1,261 @@
|
||||
#include "canvas_rendering.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include "app/gui/canvas/canvas_utils.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
namespace {
|
||||
// Constants extracted from canvas.cc
|
||||
constexpr uint32_t kRectangleColor = IM_COL32(32, 32, 32, 255);
|
||||
constexpr uint32_t kWhiteColor = IM_COL32(255, 255, 255, 255);
|
||||
} // namespace
|
||||
|
||||
void RenderCanvasBackground(
|
||||
ImDrawList* draw_list,
|
||||
const CanvasGeometry& geometry) {
|
||||
|
||||
// Draw border and background color (extracted from Canvas::DrawBackground)
|
||||
draw_list->AddRectFilled(geometry.canvas_p0, geometry.canvas_p1, kRectangleColor);
|
||||
draw_list->AddRect(geometry.canvas_p0, geometry.canvas_p1, kWhiteColor);
|
||||
}
|
||||
|
||||
void RenderCanvasGrid(
|
||||
ImDrawList* draw_list,
|
||||
const CanvasGeometry& geometry,
|
||||
const CanvasConfig& config,
|
||||
int highlight_tile_id) {
|
||||
|
||||
if (!config.enable_grid) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create render context for utility functions (extracted from Canvas::DrawGrid)
|
||||
CanvasUtils::CanvasRenderContext ctx = {
|
||||
.draw_list = draw_list,
|
||||
.canvas_p0 = geometry.canvas_p0,
|
||||
.canvas_p1 = geometry.canvas_p1,
|
||||
.scrolling = geometry.scrolling,
|
||||
.global_scale = config.global_scale,
|
||||
.enable_grid = config.enable_grid,
|
||||
.enable_hex_labels = config.enable_hex_labels,
|
||||
.grid_step = config.grid_step};
|
||||
|
||||
// Use high-level utility function
|
||||
CanvasUtils::DrawCanvasGrid(ctx, highlight_tile_id);
|
||||
}
|
||||
|
||||
void RenderCanvasOverlay(
|
||||
ImDrawList* draw_list,
|
||||
const CanvasGeometry& geometry,
|
||||
const CanvasConfig& config,
|
||||
const ImVector<ImVec2>& points,
|
||||
const ImVector<ImVec2>& selected_points) {
|
||||
|
||||
// Create render context for utility functions (extracted from Canvas::DrawOverlay)
|
||||
CanvasUtils::CanvasRenderContext ctx = {
|
||||
.draw_list = draw_list,
|
||||
.canvas_p0 = geometry.canvas_p0,
|
||||
.canvas_p1 = geometry.canvas_p1,
|
||||
.scrolling = geometry.scrolling,
|
||||
.global_scale = config.global_scale,
|
||||
.enable_grid = config.enable_grid,
|
||||
.enable_hex_labels = config.enable_hex_labels,
|
||||
.grid_step = config.grid_step};
|
||||
|
||||
// Use high-level utility function
|
||||
CanvasUtils::DrawCanvasOverlay(ctx, points, selected_points);
|
||||
}
|
||||
|
||||
void RenderCanvasLabels(
|
||||
ImDrawList* draw_list,
|
||||
const CanvasGeometry& geometry,
|
||||
const CanvasConfig& config,
|
||||
const ImVector<ImVector<std::string>>& labels,
|
||||
int current_labels,
|
||||
int tile_id_offset) {
|
||||
|
||||
if (!config.enable_custom_labels || current_labels >= labels.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Push clip rect to prevent drawing outside canvas
|
||||
draw_list->PushClipRect(geometry.canvas_p0, geometry.canvas_p1, true);
|
||||
|
||||
// Create render context for utility functions
|
||||
CanvasUtils::CanvasRenderContext ctx = {
|
||||
.draw_list = draw_list,
|
||||
.canvas_p0 = geometry.canvas_p0,
|
||||
.canvas_p1 = geometry.canvas_p1,
|
||||
.scrolling = geometry.scrolling,
|
||||
.global_scale = config.global_scale,
|
||||
.enable_grid = config.enable_grid,
|
||||
.enable_hex_labels = config.enable_hex_labels,
|
||||
.grid_step = config.grid_step};
|
||||
|
||||
// Use high-level utility function (extracted from Canvas::DrawInfoGrid)
|
||||
CanvasUtils::DrawCanvasLabels(ctx, labels, current_labels, tile_id_offset);
|
||||
|
||||
draw_list->PopClipRect();
|
||||
}
|
||||
|
||||
void RenderBitmapOnCanvas(
|
||||
ImDrawList* draw_list,
|
||||
const CanvasGeometry& geometry,
|
||||
gfx::Bitmap& bitmap,
|
||||
int /*border_offset*/,
|
||||
float scale) {
|
||||
|
||||
if (!bitmap.is_active()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Extracted from Canvas::DrawBitmap (border offset variant)
|
||||
draw_list->AddImage(
|
||||
(ImTextureID)(intptr_t)bitmap.texture(),
|
||||
ImVec2(geometry.canvas_p0.x, geometry.canvas_p0.y),
|
||||
ImVec2(geometry.canvas_p0.x + (bitmap.width() * scale),
|
||||
geometry.canvas_p0.y + (bitmap.height() * scale)));
|
||||
draw_list->AddRect(geometry.canvas_p0, geometry.canvas_p1, kWhiteColor);
|
||||
}
|
||||
|
||||
void RenderBitmapOnCanvas(
|
||||
ImDrawList* draw_list,
|
||||
const CanvasGeometry& geometry,
|
||||
gfx::Bitmap& bitmap,
|
||||
int x_offset,
|
||||
int y_offset,
|
||||
float scale,
|
||||
int alpha) {
|
||||
|
||||
if (!bitmap.is_active()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate the actual rendered size including scale and offsets
|
||||
// CRITICAL: Use scale parameter (NOT global_scale_) for per-bitmap scaling
|
||||
// Extracted from Canvas::DrawBitmap (x/y offset variant)
|
||||
ImVec2 rendered_size(bitmap.width() * scale, bitmap.height() * scale);
|
||||
|
||||
// CRITICAL FIX: Draw bitmap WITHOUT additional global_scale multiplication
|
||||
// The scale parameter already contains the correct scale factor
|
||||
// The scrolling should NOT be scaled - it's already in screen space
|
||||
draw_list->AddImage(
|
||||
(ImTextureID)(intptr_t)bitmap.texture(),
|
||||
ImVec2(geometry.canvas_p0.x + x_offset + geometry.scrolling.x,
|
||||
geometry.canvas_p0.y + y_offset + geometry.scrolling.y),
|
||||
ImVec2(geometry.canvas_p0.x + x_offset + geometry.scrolling.x + rendered_size.x,
|
||||
geometry.canvas_p0.y + y_offset + geometry.scrolling.y + rendered_size.y),
|
||||
ImVec2(0, 0), ImVec2(1, 1), IM_COL32(255, 255, 255, alpha));
|
||||
}
|
||||
|
||||
void RenderBitmapOnCanvas(
|
||||
ImDrawList* draw_list,
|
||||
const CanvasGeometry& geometry,
|
||||
gfx::Bitmap& bitmap,
|
||||
ImVec2 dest_pos,
|
||||
ImVec2 dest_size,
|
||||
ImVec2 src_pos,
|
||||
ImVec2 src_size) {
|
||||
|
||||
if (!bitmap.is_active()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Extracted from Canvas::DrawBitmap (custom source/dest regions variant)
|
||||
draw_list->AddImage(
|
||||
(ImTextureID)(intptr_t)bitmap.texture(),
|
||||
ImVec2(geometry.canvas_p0.x + dest_pos.x, geometry.canvas_p0.y + dest_pos.y),
|
||||
ImVec2(geometry.canvas_p0.x + dest_pos.x + dest_size.x,
|
||||
geometry.canvas_p0.y + dest_pos.y + dest_size.y),
|
||||
ImVec2(src_pos.x / bitmap.width(), src_pos.y / bitmap.height()),
|
||||
ImVec2((src_pos.x + src_size.x) / bitmap.width(),
|
||||
(src_pos.y + src_size.y) / bitmap.height()));
|
||||
}
|
||||
|
||||
void RenderBitmapGroup(
|
||||
ImDrawList* draw_list,
|
||||
const CanvasGeometry& geometry,
|
||||
std::vector<int>& group,
|
||||
gfx::Tilemap& tilemap,
|
||||
int tile_size,
|
||||
float scale,
|
||||
int local_map_size,
|
||||
ImVec2 total_map_size) {
|
||||
|
||||
// Extracted from Canvas::DrawBitmapGroup (lines 1148-1264)
|
||||
// This is used for multi-tile selection preview in overworld editor
|
||||
|
||||
if (group.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// OPTIMIZATION: Use optimized rendering for large groups to improve performance
|
||||
bool use_optimized_rendering = group.size() > 128;
|
||||
|
||||
// Pre-calculate common values to avoid repeated computation
|
||||
const float tile_scale = tile_size * scale;
|
||||
const int atlas_tiles_per_row = tilemap.atlas.width() / tilemap.tile_size.x;
|
||||
|
||||
// Get selected points (note: this assumes selected_points are available in context)
|
||||
// For now, we'll just render tiles at their grid positions
|
||||
// The full implementation would need the selected_points passed in
|
||||
|
||||
int i = 0;
|
||||
for (const auto tile_id : group) {
|
||||
// Calculate grid position for this tile
|
||||
int tiles_per_row = 32; // Default for standard maps
|
||||
int x = i % tiles_per_row;
|
||||
int y = i / tiles_per_row;
|
||||
|
||||
int tile_pos_x = x * tile_size * scale;
|
||||
int tile_pos_y = y * tile_size * scale;
|
||||
|
||||
// Check if tile_id is within the range
|
||||
auto tilemap_size = tilemap.map_size.x;
|
||||
if (tile_id >= 0 && tile_id < tilemap_size) {
|
||||
if (tilemap.atlas.is_active() && tilemap.atlas.texture() &&
|
||||
atlas_tiles_per_row > 0) {
|
||||
int atlas_tile_x = (tile_id % atlas_tiles_per_row) * tilemap.tile_size.x;
|
||||
int atlas_tile_y = (tile_id / atlas_tiles_per_row) * tilemap.tile_size.y;
|
||||
|
||||
// Simple bounds check
|
||||
if (atlas_tile_x >= 0 && atlas_tile_x < tilemap.atlas.width() &&
|
||||
atlas_tile_y >= 0 && atlas_tile_y < tilemap.atlas.height()) {
|
||||
|
||||
// Calculate UV coordinates once for efficiency
|
||||
const float atlas_width = static_cast<float>(tilemap.atlas.width());
|
||||
const float atlas_height = static_cast<float>(tilemap.atlas.height());
|
||||
ImVec2 uv0 = ImVec2(atlas_tile_x / atlas_width, atlas_tile_y / atlas_height);
|
||||
ImVec2 uv1 = ImVec2((atlas_tile_x + tilemap.tile_size.x) / atlas_width,
|
||||
(atlas_tile_y + tilemap.tile_size.y) / atlas_height);
|
||||
|
||||
// Calculate screen positions
|
||||
float screen_x = geometry.canvas_p0.x + geometry.scrolling.x + tile_pos_x;
|
||||
float screen_y = geometry.canvas_p0.y + geometry.scrolling.y + tile_pos_y;
|
||||
float screen_w = tilemap.tile_size.x * scale;
|
||||
float screen_h = tilemap.tile_size.y * scale;
|
||||
|
||||
// Use higher alpha for large selections to make them more visible
|
||||
uint32_t alpha_color = use_optimized_rendering
|
||||
? IM_COL32(255, 255, 255, 200)
|
||||
: IM_COL32(255, 255, 255, 150);
|
||||
|
||||
// Draw from atlas texture with optimized parameters
|
||||
draw_list->AddImage(
|
||||
(ImTextureID)(intptr_t)tilemap.atlas.texture(),
|
||||
ImVec2(screen_x, screen_y),
|
||||
ImVec2(screen_x + screen_w, screen_y + screen_h),
|
||||
uv0, uv1, alpha_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
179
src/app/gui/canvas/canvas_rendering.h
Normal file
179
src/app/gui/canvas/canvas_rendering.h
Normal file
@@ -0,0 +1,179 @@
|
||||
#ifndef YAZE_APP_GUI_CANVAS_CANVAS_RENDERING_H
|
||||
#define YAZE_APP_GUI_CANVAS_CANVAS_RENDERING_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "app/gfx/core/bitmap.h"
|
||||
#include "app/gfx/render/tilemap.h"
|
||||
#include "app/gui/canvas/canvas_state.h"
|
||||
#include "app/gui/canvas/canvas_utils.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
/**
|
||||
* @brief Render canvas background and border
|
||||
*
|
||||
* Draws the canvas background rectangle (dark) and border (white)
|
||||
* at the calculated geometry positions. Extracted from Canvas::DrawBackground().
|
||||
*
|
||||
* @param draw_list ImGui draw list for rendering
|
||||
* @param geometry Canvas geometry for this frame
|
||||
*/
|
||||
void RenderCanvasBackground(
|
||||
ImDrawList* draw_list,
|
||||
const CanvasGeometry& geometry);
|
||||
|
||||
/**
|
||||
* @brief Render canvas grid with optional highlighting
|
||||
*
|
||||
* Draws grid lines, hex labels, and optional tile highlighting.
|
||||
* Extracted from Canvas::DrawGrid().
|
||||
*
|
||||
* @param draw_list ImGui draw list for rendering
|
||||
* @param geometry Canvas geometry
|
||||
* @param config Canvas configuration (grid settings)
|
||||
* @param highlight_tile_id Tile ID to highlight (-1 = no highlight)
|
||||
*/
|
||||
void RenderCanvasGrid(
|
||||
ImDrawList* draw_list,
|
||||
const CanvasGeometry& geometry,
|
||||
const CanvasConfig& config,
|
||||
int highlight_tile_id = -1);
|
||||
|
||||
/**
|
||||
* @brief Render canvas overlay (hover and selection points)
|
||||
*
|
||||
* Draws hover preview points and selection rectangle points.
|
||||
* Extracted from Canvas::DrawOverlay().
|
||||
*
|
||||
* @param draw_list ImGui draw list for rendering
|
||||
* @param geometry Canvas geometry
|
||||
* @param config Canvas configuration (scale)
|
||||
* @param points Hover preview points
|
||||
* @param selected_points Selection rectangle points
|
||||
*/
|
||||
void RenderCanvasOverlay(
|
||||
ImDrawList* draw_list,
|
||||
const CanvasGeometry& geometry,
|
||||
const CanvasConfig& config,
|
||||
const ImVector<ImVec2>& points,
|
||||
const ImVector<ImVec2>& selected_points);
|
||||
|
||||
/**
|
||||
* @brief Render canvas labels on grid
|
||||
*
|
||||
* Draws custom text labels on canvas tiles.
|
||||
* Extracted from Canvas::DrawInfoGrid().
|
||||
*
|
||||
* @param draw_list ImGui draw list for rendering
|
||||
* @param geometry Canvas geometry
|
||||
* @param config Canvas configuration
|
||||
* @param labels Label arrays (one per label set)
|
||||
* @param current_labels Active label set index
|
||||
* @param tile_id_offset Tile ID offset for calculation
|
||||
*/
|
||||
void RenderCanvasLabels(
|
||||
ImDrawList* draw_list,
|
||||
const CanvasGeometry& geometry,
|
||||
const CanvasConfig& config,
|
||||
const ImVector<ImVector<std::string>>& labels,
|
||||
int current_labels,
|
||||
int tile_id_offset);
|
||||
|
||||
/**
|
||||
* @brief Render bitmap on canvas (border offset variant)
|
||||
*
|
||||
* Draws a bitmap with a border offset from canvas origin.
|
||||
* Extracted from Canvas::DrawBitmap().
|
||||
*
|
||||
* @param draw_list ImGui draw list
|
||||
* @param geometry Canvas geometry
|
||||
* @param bitmap Bitmap to render
|
||||
* @param border_offset Offset from canvas edges
|
||||
* @param scale Rendering scale
|
||||
*/
|
||||
void RenderBitmapOnCanvas(
|
||||
ImDrawList* draw_list,
|
||||
const CanvasGeometry& geometry,
|
||||
gfx::Bitmap& bitmap,
|
||||
int border_offset,
|
||||
float scale);
|
||||
|
||||
/**
|
||||
* @brief Render bitmap on canvas (x/y offset variant)
|
||||
*
|
||||
* Draws a bitmap at specified x/y offset with optional alpha.
|
||||
* Extracted from Canvas::DrawBitmap().
|
||||
*
|
||||
* @param draw_list ImGui draw list
|
||||
* @param geometry Canvas geometry
|
||||
* @param bitmap Bitmap to render
|
||||
* @param x_offset X offset from canvas origin
|
||||
* @param y_offset Y offset from canvas origin
|
||||
* @param scale Rendering scale
|
||||
* @param alpha Alpha transparency (0-255)
|
||||
*/
|
||||
void RenderBitmapOnCanvas(
|
||||
ImDrawList* draw_list,
|
||||
const CanvasGeometry& geometry,
|
||||
gfx::Bitmap& bitmap,
|
||||
int x_offset,
|
||||
int y_offset,
|
||||
float scale,
|
||||
int alpha);
|
||||
|
||||
/**
|
||||
* @brief Render bitmap on canvas (custom source/dest regions)
|
||||
*
|
||||
* Draws a bitmap with explicit source and destination rectangles.
|
||||
* Extracted from Canvas::DrawBitmap().
|
||||
*
|
||||
* @param draw_list ImGui draw list
|
||||
* @param geometry Canvas geometry
|
||||
* @param bitmap Bitmap to render
|
||||
* @param dest_pos Destination position (canvas-relative)
|
||||
* @param dest_size Destination size
|
||||
* @param src_pos Source position in bitmap
|
||||
* @param src_size Source size in bitmap
|
||||
*/
|
||||
void RenderBitmapOnCanvas(
|
||||
ImDrawList* draw_list,
|
||||
const CanvasGeometry& geometry,
|
||||
gfx::Bitmap& bitmap,
|
||||
ImVec2 dest_pos,
|
||||
ImVec2 dest_size,
|
||||
ImVec2 src_pos,
|
||||
ImVec2 src_size);
|
||||
|
||||
/**
|
||||
* @brief Render group of bitmaps from tilemap
|
||||
*
|
||||
* Draws multiple tiles for multi-tile selection preview.
|
||||
* Extracted from Canvas::DrawBitmapGroup().
|
||||
*
|
||||
* @param draw_list ImGui draw list
|
||||
* @param geometry Canvas geometry
|
||||
* @param group Vector of tile IDs to draw
|
||||
* @param tilemap Tilemap containing the tiles
|
||||
* @param tile_size Size of each tile
|
||||
* @param scale Rendering scale
|
||||
* @param local_map_size Size of local map in pixels (default 512)
|
||||
* @param total_map_size Total map size for boundary clamping
|
||||
*/
|
||||
void RenderBitmapGroup(
|
||||
ImDrawList* draw_list,
|
||||
const CanvasGeometry& geometry,
|
||||
std::vector<int>& group,
|
||||
gfx::Tilemap& tilemap,
|
||||
int tile_size,
|
||||
float scale,
|
||||
int local_map_size,
|
||||
ImVec2 total_map_size);
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_GUI_CANVAS_CANVAS_RENDERING_H
|
||||
|
||||
79
src/app/gui/canvas/canvas_state.h
Normal file
79
src/app/gui/canvas/canvas_state.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#ifndef YAZE_APP_GUI_CANVAS_CANVAS_STATE_H
|
||||
#define YAZE_APP_GUI_CANVAS_CANVAS_STATE_H
|
||||
|
||||
#include <string>
|
||||
#include "app/gui/canvas/canvas_utils.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
/**
|
||||
* @brief Canvas geometry calculated per-frame
|
||||
*
|
||||
* Represents the position, size, and scroll state of a canvas
|
||||
* in both screen space and scaled space. Used by rendering
|
||||
* functions to correctly position elements.
|
||||
*/
|
||||
struct CanvasGeometry {
|
||||
ImVec2 canvas_p0; // Top-left screen position
|
||||
ImVec2 canvas_p1; // Bottom-right screen position
|
||||
ImVec2 canvas_sz; // Actual canvas size (unscaled)
|
||||
ImVec2 scaled_size; // Size after applying global_scale
|
||||
ImVec2 scrolling; // Current scroll offset
|
||||
|
||||
CanvasGeometry()
|
||||
: canvas_p0(0, 0), canvas_p1(0, 0), canvas_sz(0, 0),
|
||||
scaled_size(0, 0), scrolling(0, 0) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Complete canvas state snapshot
|
||||
*
|
||||
* Aggregates all canvas state into a single POD for easier
|
||||
* refactoring and testing. Designed to replace the scattered
|
||||
* state members in the Canvas class gradually.
|
||||
*
|
||||
* Usage Pattern:
|
||||
* - Geometry recalculated every frame in DrawBackground()
|
||||
* - Configuration updated via user interaction
|
||||
* - Selection state modified by interaction handlers
|
||||
* - Drawing state (points, labels) updated during rendering
|
||||
*/
|
||||
struct CanvasState {
|
||||
// Core identification
|
||||
std::string canvas_id = "Canvas";
|
||||
std::string context_id = "CanvasContext";
|
||||
|
||||
// Configuration (reference existing CanvasConfig)
|
||||
CanvasConfig config;
|
||||
|
||||
// Selection (reference existing CanvasSelection)
|
||||
CanvasSelection selection;
|
||||
|
||||
// Geometry (calculated per-frame)
|
||||
CanvasGeometry geometry;
|
||||
|
||||
// Interaction state
|
||||
ImVec2 mouse_pos_in_canvas = ImVec2(0, 0);
|
||||
ImVec2 drawn_tile_pos = ImVec2(-1, -1);
|
||||
bool is_hovered = false;
|
||||
|
||||
// Drawing state
|
||||
ImVector<ImVec2> points; // Hover preview points
|
||||
ImVector<ImVector<std::string>> labels;
|
||||
int current_labels = 0;
|
||||
int highlight_tile_id = -1;
|
||||
|
||||
CanvasState() = default;
|
||||
|
||||
// Convenience constructor with ID
|
||||
explicit CanvasState(const std::string& id)
|
||||
: canvas_id(id), context_id(id + "Context") {}
|
||||
};
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_GUI_CANVAS_CANVAS_STATE_H
|
||||
|
||||
@@ -29,9 +29,11 @@ set(CANVAS_SRC
|
||||
app/gui/canvas/canvas.cc
|
||||
app/gui/canvas/canvas_automation_api.cc
|
||||
app/gui/canvas/canvas_context_menu.cc
|
||||
app/gui/canvas/canvas_geometry.cc
|
||||
app/gui/canvas/canvas_interaction_handler.cc
|
||||
app/gui/canvas/canvas_modals.cc
|
||||
app/gui/canvas/canvas_performance_integration.cc
|
||||
app/gui/canvas/canvas_rendering.cc
|
||||
app/gui/canvas/canvas_usage_tracker.cc
|
||||
app/gui/canvas/canvas_utils.cc
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user