Canvas, Palettes, Bitmap updates

This commit is contained in:
scawful
2023-12-25 17:32:56 -06:00
parent 4d05f95312
commit a73c944529
11 changed files with 208 additions and 129 deletions

View File

@@ -285,15 +285,15 @@ void DungeonEditor::DrawRoomGraphics() {
int current_block = 0; int current_block = 0;
for (int block : blocks) { for (int block : blocks) {
int offset = height * (current_block + 1); int offset = height * (current_block + 1);
int top_left_y = room_gfx_canvas_.GetZeroPoint().y + 2; int top_left_y = room_gfx_canvas_.zero_point().y + 2;
if (current_block >= 1) { if (current_block >= 1) {
top_left_y = room_gfx_canvas_.GetZeroPoint().y + height * current_block; top_left_y = room_gfx_canvas_.zero_point().y + height * current_block;
} }
room_gfx_canvas_.GetDrawList()->AddImage( room_gfx_canvas_.GetDrawList()->AddImage(
(void*)graphics_bin_[block].texture(), (void*)graphics_bin_[block].texture(),
ImVec2(room_gfx_canvas_.GetZeroPoint().x + 2, top_left_y), ImVec2(room_gfx_canvas_.zero_point().x + 2, top_left_y),
ImVec2(room_gfx_canvas_.GetZeroPoint().x + 0x100, ImVec2(room_gfx_canvas_.zero_point().x + 0x100,
room_gfx_canvas_.GetZeroPoint().y + offset)); room_gfx_canvas_.zero_point().y + offset));
current_block += 1; current_block += 1;
} }
} }

View File

@@ -142,13 +142,21 @@ void GraphicsEditor::DrawGfxEditToolset() {
} }
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::ColorEdit4("Palette Color", (float*)&current_color_, auto bitmap = rom()->bitmap_manager()[current_sheet_];
ImGuiColorEditFlags_NoInputs | auto palette = bitmap->palette();
ImGuiColorEditFlags_NoLabel | for (int i = 0; i < 8; i++) {
ImGuiColorEditFlags_NoAlpha); ImGui::SameLine();
auto color =
ImVec4(palette[i].GetRGB().x / 255.0f, palette[i].GetRGB().y / 255.0f,
palette[i].GetRGB().z / 255.0f, 255.0f);
if (ImGui::ColorButton(absl::StrFormat("Palette Color %d", i).c_str(),
color)) {
current_color_ = color;
}
}
ImGui::TableNextColumn(); ImGui::TableNextColumn();
gui::InputHexByte("Tile Size", &tile_size_, 0x02); gui::InputHexByte("Tile Size", &tile_size_, 0x01);
ImGui::EndTable(); ImGui::EndTable();
} }
@@ -170,11 +178,11 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() {
auto texture = value.get()->texture(); auto texture = value.get()->texture();
graphics_bin_canvas_.GetDrawList()->AddImage( graphics_bin_canvas_.GetDrawList()->AddImage(
(void*)texture, (void*)texture,
ImVec2(graphics_bin_canvas_.GetZeroPoint().x + 2, ImVec2(graphics_bin_canvas_.zero_point().x + 2,
graphics_bin_canvas_.GetZeroPoint().y + 2), graphics_bin_canvas_.zero_point().y + 2),
ImVec2(graphics_bin_canvas_.GetZeroPoint().x + ImVec2(graphics_bin_canvas_.zero_point().x +
value.get()->width() * sheet_scale_, value.get()->width() * sheet_scale_,
graphics_bin_canvas_.GetZeroPoint().y + graphics_bin_canvas_.zero_point().y +
value.get()->height() * sheet_scale_)); value.get()->height() * sheet_scale_));
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
@@ -183,8 +191,8 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() {
} }
// Add a slightly transparent rectangle behind the text // Add a slightly transparent rectangle behind the text
ImVec2 text_pos(graphics_bin_canvas_.GetZeroPoint().x + 2, ImVec2 text_pos(graphics_bin_canvas_.zero_point().x + 2,
graphics_bin_canvas_.GetZeroPoint().y + 2); graphics_bin_canvas_.zero_point().y + 2);
ImVec2 text_size = ImVec2 text_size =
ImGui::CalcTextSize(absl::StrFormat("%02X", key).c_str()); ImGui::CalcTextSize(absl::StrFormat("%02X", key).c_str());
ImVec2 rent_min(text_pos.x, text_pos.y); ImVec2 rent_min(text_pos.x, text_pos.y);
@@ -220,11 +228,10 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
} }
for (auto& sheet_id : open_sheets_) { for (auto& sheet_id : open_sheets_) {
current_sheet_ = sheet_id;
bool open = true; bool open = true;
if (ImGui::BeginTabItem(absl::StrFormat("%d", sheet_id).c_str(), &open, if (ImGui::BeginTabItem(absl::StrFormat("%d", sheet_id).c_str(), &open,
ImGuiTabItemFlags_None)) { ImGuiTabItemFlags_None)) {
current_sheet_ = sheet_id;
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
release_queue_.push(sheet_id); release_queue_.push(sheet_id);
} }
@@ -243,18 +250,17 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
ImGuiWindowFlags_AlwaysHorizontalScrollbar); ImGuiWindowFlags_AlwaysHorizontalScrollbar);
auto draw_tile_event = [&]() { auto draw_tile_event = [&]() {
uint16_t snes_color = gfx::ConvertRGBtoSNES(current_color_); gfx::Bitmap& current_bitmap =
auto click_position = current_sheet_canvas_.drawn_tile_position(); *rom()->mutable_bitmap_manager()->mutable_bitmap(sheet_id);
gfx::Bitmap& current_bitmap = *rom()->bitmap_manager()[sheet_id]; current_sheet_canvas_.DrawTileOnBitmap(tile_size_, current_bitmap,
current_sheet_canvas_.DrawTileOnBitmap(click_position, tile_size_, current_color_);
current_bitmap, snes_color); rom()->UpdateBitmap(&current_bitmap);
auto& bitmap = *rom()->bitmap_manager().mutable_bitmap(sheet_id);
rom()->UpdateBitmap(&bitmap);
}; };
auto size = ImVec2(0x80, 0x20);
current_sheet_canvas_.UpdateColorPainter( current_sheet_canvas_.UpdateColorPainter(
*rom()->bitmap_manager()[sheet_id], current_color_, draw_tile_event, *rom()->bitmap_manager()[sheet_id], current_color_, draw_tile_event,
ImVec2(0x100, 0x40), tile_size_, current_scale_, 8.0f); size, tile_size_, current_scale_, 8.0f);
ImGui::EndChild(); ImGui::EndChild();
ImGui::EndTabItem(); ImGui::EndTabItem();
} }

View File

@@ -106,7 +106,7 @@ class GraphicsEditor : public SharedROM {
// Member Variables // Member Variables
ImVec4 current_color_; ImVec4 current_color_;
uint16_t current_sheet_ = 0; uint16_t current_sheet_ = 0;
uint8_t tile_size_ = 0x08; uint8_t tile_size_ = 0x01;
std::set<uint16_t> open_sheets_; std::set<uint16_t> open_sheets_;
std::set<uint16_t> child_window_sheets_; std::set<uint16_t> child_window_sheets_;
std::stack<uint16_t> release_queue_; std::stack<uint16_t> release_queue_;

View File

@@ -27,6 +27,7 @@ namespace yaze {
namespace app { namespace app {
namespace editor { namespace editor {
using ImGui::Button;
using ImGui::Separator; using ImGui::Separator;
using ImGui::TableHeadersRow; using ImGui::TableHeadersRow;
using ImGui::TableNextColumn; using ImGui::TableNextColumn;
@@ -93,10 +94,19 @@ absl::Status OverworldEditor::DrawToolset() {
status_ = Redo(); status_ = Redo();
} }
TEXT_COLUMN(ICON_MD_MORE_VERT) // Separator TEXT_COLUMN(ICON_MD_MORE_VERT) // Separator
BUTTON_COLUMN(ICON_MD_ZOOM_OUT) // Zoom Out
BUTTON_COLUMN(ICON_MD_ZOOM_IN) // Zoom In NEXT_COLUMN()
TEXT_COLUMN(ICON_MD_MORE_VERT) // Separator if (ImGui::Button(ICON_MD_ZOOM_OUT)) {
ow_map_canvas_.ZoomOut();
}
NEXT_COLUMN()
if (ImGui::Button(ICON_MD_ZOOM_IN)) {
ow_map_canvas_.ZoomIn();
}
TEXT_COLUMN(ICON_MD_MORE_VERT) // Separator
NEXT_COLUMN() NEXT_COLUMN()
if (ImGui::Button(ICON_MD_DRAW)) { if (ImGui::Button(ICON_MD_DRAW)) {
@@ -288,24 +298,6 @@ bool OverworldEditor::IsMouseHoveringOverEntrance(
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void OverworldEditor::DrawOverworldMaps() {
int xx = 0;
int yy = 0;
for (int i = 0; i < 0x40; i++) {
int world_index = i + (current_world_ * 0x40);
int map_x = (xx * 0x200);
int map_y = (yy * 0x200);
ow_map_canvas_.DrawBitmap(maps_bmp_[world_index], map_x, map_y);
xx++;
if (xx >= 8) {
yy++;
xx = 0;
}
}
}
// ----------------------------------------------------------------------------
void OverworldEditor::DrawOverworldSprites() { void OverworldEditor::DrawOverworldSprites() {
for (const auto &sprite : overworld_.Sprites(game_state_)) { for (const auto &sprite : overworld_.Sprites(game_state_)) {
// Get the sprite's bitmap and real X and Y positions // Get the sprite's bitmap and real X and Y positions
@@ -325,12 +317,28 @@ void OverworldEditor::DrawOverworldSprites() {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void OverworldEditor::DrawOverworldMaps() {
int xx = 0;
int yy = 0;
for (int i = 0; i < 0x40; i++) {
int world_index = i + (current_world_ * 0x40);
int map_x = (xx * 0x200 * ow_map_canvas_.global_scale());
int map_y = (yy * 0x200 * ow_map_canvas_.global_scale());
ow_map_canvas_.DrawBitmap(maps_bmp_[world_index], map_x, map_y,
ow_map_canvas_.global_scale());
xx++;
if (xx >= 8) {
yy++;
xx = 0;
}
}
}
void OverworldEditor::DrawOverworldEdits() { void OverworldEditor::DrawOverworldEdits() {
auto mouse_position = ow_map_canvas_.drawn_tile_position(); auto mouse_position = ow_map_canvas_.drawn_tile_position();
auto canvas_size = ow_map_canvas_.GetCanvasSize(); auto canvas_size = ow_map_canvas_.canvas_size();
int x = mouse_position.x / canvas_size.x; int x = mouse_position.x / canvas_size.x;
int y = mouse_position.y / canvas_size.y; int y = mouse_position.y / canvas_size.y;
auto index = x + (y * 8);
// Determine which overworld map the user is currently editing. // Determine which overworld map the user is currently editing.
DetermineActiveMap(mouse_position); DetermineActiveMap(mouse_position);
@@ -338,9 +346,6 @@ void OverworldEditor::DrawOverworldEdits() {
// Render the updated map bitmap. // Render the updated map bitmap.
RenderUpdatedMapBitmap(mouse_position, RenderUpdatedMapBitmap(mouse_position,
tile16_individual_data_[current_tile16_]); tile16_individual_data_[current_tile16_]);
// Queue up the raw ROM changes.
QueueROMChanges(index, current_tile16_);
} }
void OverworldEditor::DetermineActiveMap(const ImVec2 &mouse_position) { void OverworldEditor::DetermineActiveMap(const ImVec2 &mouse_position) {
@@ -385,7 +390,7 @@ void OverworldEditor::RenderUpdatedMapBitmap(const ImVec2 &click_position,
rom()->UpdateBitmap(&current_bitmap); rom()->UpdateBitmap(&current_bitmap);
} }
void OverworldEditor::QueueROMChanges(int index, ushort new_tile16) { void OverworldEditor::SaveOverworldChanges() {
// Store the changes made by the user to the ROM (or project file) // Store the changes made by the user to the ROM (or project file)
rom()->QueueChanges([&]() { rom()->QueueChanges([&]() {
PRINT_IF_ERROR(overworld_.SaveOverworldMaps()); PRINT_IF_ERROR(overworld_.SaveOverworldMaps());
@@ -398,8 +403,6 @@ void OverworldEditor::QueueROMChanges(int index, ushort new_tile16) {
}); });
} }
// ----------------------------------------------------------------------------
void OverworldEditor::CheckForOverworldEdits() { void OverworldEditor::CheckForOverworldEdits() {
if (!blockset_canvas_.Points().empty()) { if (!blockset_canvas_.Points().empty()) {
// User has selected a tile they want to draw from the blockset. // User has selected a tile they want to draw from the blockset.
@@ -471,10 +474,11 @@ void OverworldEditor::DrawOverworldCanvas() {
ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysVerticalScrollbar |
ImGuiWindowFlags_AlwaysHorizontalScrollbar)) { ImGuiWindowFlags_AlwaysHorizontalScrollbar)) {
ow_map_canvas_.DrawBackground(ImVec2(0x200 * 8, 0x200 * 8)); ow_map_canvas_.DrawBackground(ImVec2(0x200 * 8, 0x200 * 8));
ImGui::PopStyleVar(2);
ow_map_canvas_.DrawContextMenu(); ow_map_canvas_.DrawContextMenu();
if (overworld_.isLoaded()) { if (overworld_.isLoaded()) {
DrawOverworldMaps(); DrawOverworldMaps();
DrawOverworldEntrances(ow_map_canvas_.GetZeroPoint(), DrawOverworldEntrances(ow_map_canvas_.zero_point(),
ow_map_canvas_.Scrolling()); ow_map_canvas_.Scrolling());
if (flags()->kDrawOverworldSprites) { if (flags()->kDrawOverworldSprites) {
DrawOverworldSprites(); DrawOverworldSprites();
@@ -486,11 +490,8 @@ void OverworldEditor::DrawOverworldCanvas() {
ow_map_canvas_.DrawOverlay(); ow_map_canvas_.DrawOverlay();
} }
ImGui::EndChild(); ImGui::EndChild();
ImGui::PopStyleVar(2);
} }
// ----------------------------------------------------------------------------
// Tile 8 Selector // Tile 8 Selector
// Displays all the individual tiles that make up a tile16. // Displays all the individual tiles that make up a tile16.
void OverworldEditor::DrawTile8Selector() { void OverworldEditor::DrawTile8Selector() {
@@ -501,24 +502,22 @@ void OverworldEditor::DrawTile8Selector() {
// for (const auto &[key, value] : graphics_bin_) { // for (const auto &[key, value] : graphics_bin_) {
for (auto &[key, value] : rom()->bitmap_manager()) { for (auto &[key, value] : rom()->bitmap_manager()) {
int offset = 0x40 * (key + 1); int offset = 0x40 * (key + 1);
int top_left_y = graphics_bin_canvas_.GetZeroPoint().y + 2; int top_left_y = graphics_bin_canvas_.zero_point().y + 2;
if (key >= 1) { if (key >= 1) {
top_left_y = graphics_bin_canvas_.GetZeroPoint().y + 0x40 * key; top_left_y = graphics_bin_canvas_.zero_point().y + 0x40 * key;
} }
auto texture = value.get()->texture(); auto texture = value.get()->texture();
graphics_bin_canvas_.GetDrawList()->AddImage( graphics_bin_canvas_.GetDrawList()->AddImage(
(void *)texture, (void *)texture,
ImVec2(graphics_bin_canvas_.GetZeroPoint().x + 2, top_left_y), ImVec2(graphics_bin_canvas_.zero_point().x + 2, top_left_y),
ImVec2(graphics_bin_canvas_.GetZeroPoint().x + 0x100, ImVec2(graphics_bin_canvas_.zero_point().x + 0x100,
graphics_bin_canvas_.GetZeroPoint().y + offset)); graphics_bin_canvas_.zero_point().y + offset));
} }
} }
graphics_bin_canvas_.DrawGrid(16.0f); graphics_bin_canvas_.DrawGrid(16.0f);
graphics_bin_canvas_.DrawOverlay(); graphics_bin_canvas_.DrawOverlay();
} }
// ----------------------------------------------------------------------------
void OverworldEditor::DrawTileSelector() { void OverworldEditor::DrawTileSelector() {
if (ImGui::BeginTabBar(kTileSelectorTab.data(), if (ImGui::BeginTabBar(kTileSelectorTab.data(),
ImGuiTabBarFlags_FittingPolicyScroll)) { ImGuiTabBarFlags_FittingPolicyScroll)) {

View File

@@ -98,7 +98,7 @@ class OverworldEditor : public Editor,
void DrawOverworldEdits(); void DrawOverworldEdits();
void RenderUpdatedMapBitmap(const ImVec2 &click_position, void RenderUpdatedMapBitmap(const ImVec2 &click_position,
const Bytes &tile_data); const Bytes &tile_data);
void QueueROMChanges(int index, ushort new_tile16); void SaveOverworldChanges();
void DetermineActiveMap(const ImVec2 &mouse_position); void DetermineActiveMap(const ImVec2 &mouse_position);
void CheckForOverworldEdits(); void CheckForOverworldEdits();
@@ -109,7 +109,6 @@ class OverworldEditor : public Editor,
void DrawTileSelector(); void DrawTileSelector();
absl::Status LoadSpriteGraphics(); absl::Status LoadSpriteGraphics();
absl::Status DrawExperimentalModal(); absl::Status DrawExperimentalModal();
enum class EditingMode { enum class EditingMode {

View File

@@ -348,8 +348,8 @@ void Bitmap::ApplyPalette(const SNESPalette &palette) {
void Bitmap::ApplyPaletteWithTransparent(const SNESPalette &palette, void Bitmap::ApplyPaletteWithTransparent(const SNESPalette &palette,
int index) { int index) {
palette_ = palette;
auto start_index = index * 7; auto start_index = index * 7;
palette_ = palette.sub_palette(start_index, start_index + 7);
std::vector<ImVec4> colors; std::vector<ImVec4> colors;
colors.push_back(ImVec4(0, 0, 0, 0)); colors.push_back(ImVec4(0, 0, 0, 0));
for (int i = start_index; i < start_index + 7; ++i) { for (int i = start_index; i < start_index + 7; ++i) {

View File

@@ -81,6 +81,40 @@ class Bitmap {
} }
} }
Uint8 ConvertImVec4ToIndex8(const ImVec4 &color, SDL_Surface *surface) {
if (surface->format->format != SDL_PIXELFORMAT_INDEX8) {
throw std::runtime_error(
"Surface is not in SDL_PIXELFORMAT_INDEX8 format.");
}
// Convert ImVec4 (RGBA) to SDL_Color (RGBA)
SDL_Color sdl_color;
sdl_color.r = static_cast<Uint8>(color.x * 255);
sdl_color.g = static_cast<Uint8>(color.y * 255);
sdl_color.b = static_cast<Uint8>(color.z * 255);
sdl_color.a = static_cast<Uint8>(color.w * 255);
// Map SDL_Color to the nearest color index in the surface's palette
return SDL_MapRGB(surface->format, sdl_color.r, sdl_color.g, sdl_color.b);
}
void WriteColor(int position, const ImVec4 &color) {
// Convert ImVec4 (RGBA) to SDL_Color (RGBA)
SDL_Color sdl_color;
sdl_color.r = static_cast<Uint8>(color.x * 255);
sdl_color.g = static_cast<Uint8>(color.y * 255);
sdl_color.b = static_cast<Uint8>(color.z * 255);
sdl_color.a = static_cast<Uint8>(color.w * 255);
// Map SDL_Color to the nearest color index in the surface's palette
Uint8 index =
SDL_MapRGB(surface_->format, sdl_color.r, sdl_color.g, sdl_color.b);
// Write the color index to the pixel data
this->pixel_data_[position] = index;
modified_ = true;
}
void Cleanup() { void Cleanup() {
// Reset texture_ // Reset texture_
if (texture_) { if (texture_) {
@@ -106,6 +140,15 @@ class Bitmap {
palette_.Clear(); palette_.Clear();
} }
auto sdl_palette() {
if (surface_ == nullptr) {
throw std::runtime_error("Surface is null.");
}
return surface_->format->palette;
}
auto palette() const { return palette_; }
auto palette_size() const { return palette_.size(); }
int width() const { return width_; } int width() const { return width_; }
int height() const { return height_; } int height() const { return height_; }
auto depth() const { return depth_; } auto depth() const { return depth_; }

View File

@@ -187,6 +187,14 @@ class SNESPalette {
colors[i].SetModified(true); colors[i].SetModified(true);
} }
SNESPalette sub_palette(int start, int end) const {
SNESPalette pal;
for (int i = start; i < end; i++) {
pal.AddColor(colors[i]);
}
return pal;
}
private: private:
int size_ = 0; /**< The size of the palette. */ int size_ = 0; /**< The size of the palette. */
std::vector<SNESColor> colors; /**< The colors in the palette. */ std::vector<SNESColor> colors; /**< The colors in the palette. */

View File

@@ -36,10 +36,6 @@ void Canvas::UpdateColorPainter(const gfx::Bitmap &bitmap, const ImVec4 &color,
ImVec2 bg_size, int tile_size, float scale, ImVec2 bg_size, int tile_size, float scale,
float grid_size) { float grid_size) {
global_scale_ = scale; global_scale_ = scale;
if (scale != 1.0f) {
bg_size.x *= scale / 2;
bg_size.y *= scale / 2;
}
DrawBackground(bg_size); DrawBackground(bg_size);
DrawContextMenu(); DrawContextMenu();
DrawBitmap(bitmap, 2, scale); DrawBitmap(bitmap, 2, scale);
@@ -63,7 +59,8 @@ void Canvas::DrawBackground(ImVec2 canvas_size) {
canvas_p0_ = ImGui::GetCursorScreenPos(); canvas_p0_ = ImGui::GetCursorScreenPos();
if (!custom_canvas_size_) canvas_sz_ = ImGui::GetContentRegionAvail(); if (!custom_canvas_size_) canvas_sz_ = ImGui::GetContentRegionAvail();
if (canvas_size.x != 0) canvas_sz_ = canvas_size; if (canvas_size.x != 0) canvas_sz_ = canvas_size;
canvas_p1_ = ImVec2(canvas_p0_.x + canvas_sz_.x, canvas_p0_.y + canvas_sz_.y); canvas_p1_ = ImVec2(canvas_p0_.x + (canvas_sz_.x * global_scale_),
canvas_p0_.y + (canvas_sz_.y * global_scale_));
draw_list_ = ImGui::GetWindowDrawList(); // Draw border and background color draw_list_ = ImGui::GetWindowDrawList(); // Draw border and background color
draw_list_->AddRectFilled(canvas_p0_, canvas_p1_, kRectangleColor); draw_list_->AddRectFilled(canvas_p0_, canvas_p1_, kRectangleColor);
draw_list_->AddRect(canvas_p0_, canvas_p1_, kRectangleBorder); draw_list_->AddRect(canvas_p0_, canvas_p1_, kRectangleBorder);
@@ -71,7 +68,9 @@ void Canvas::DrawBackground(ImVec2 canvas_size) {
void Canvas::DrawContextMenu() { void Canvas::DrawContextMenu() {
const ImGuiIO &io = ImGui::GetIO(); const ImGuiIO &io = ImGui::GetIO();
ImGui::InvisibleButton("canvas", canvas_sz_, kMouseFlags); auto scaled_sz =
ImVec2(canvas_sz_.x * global_scale_, canvas_sz_.y * global_scale_);
ImGui::InvisibleButton("canvas", scaled_sz, kMouseFlags);
const bool is_active = ImGui::IsItemActive(); // Held const bool is_active = ImGui::IsItemActive(); // Held
const ImVec2 origin(canvas_p0_.x + scrolling_.x, const ImVec2 origin(canvas_p0_.x + scrolling_.x,
canvas_p0_.y + scrolling_.y); // Lock scrolled origin canvas_p0_.y + scrolling_.y); // Lock scrolled origin
@@ -94,6 +93,11 @@ void Canvas::DrawContextMenu() {
if (ImGui::BeginPopup("context")) { if (ImGui::BeginPopup("context")) {
ImGui::MenuItem("Show Grid", nullptr, &enable_grid_); ImGui::MenuItem("Show Grid", nullptr, &enable_grid_);
ImGui::Selectable("Show Labels", &enable_hex_tile_labels_); ImGui::Selectable("Show Labels", &enable_hex_tile_labels_);
if (ImGui::MenuItem("Reset Position", nullptr, false)) {
scrolling_.x = 0;
scrolling_.y = 0;
}
ImGui::Separator();
if (ImGui::MenuItem("8x8", nullptr, custom_step_ == 8.0f)) { if (ImGui::MenuItem("8x8", nullptr, custom_step_ == 8.0f)) {
custom_step_ = 8.0f; custom_step_ = 8.0f;
} }
@@ -106,10 +110,12 @@ void Canvas::DrawContextMenu() {
if (ImGui::MenuItem("64x64", nullptr, custom_step_ == 64.0f)) { if (ImGui::MenuItem("64x64", nullptr, custom_step_ == 64.0f)) {
custom_step_ = 64.0f; custom_step_ = 64.0f;
} }
if (ImGui::MenuItem("Reset Position", nullptr, false)) { // Display bitmap metadata such as canvas size and global scale
scrolling_.x = 0; ImGui::Separator();
scrolling_.y = 0; ImGui::Text("Canvas Size: %.0f x %.0f", canvas_sz_.x, canvas_sz_.y);
} ImGui::Text("Global Scale: %.1f", global_scale_);
ImGui::Text("Mouse Position: %.0f x %.0f", mouse_pos.x, mouse_pos.y);
ImGui::EndPopup(); ImGui::EndPopup();
} }
} }
@@ -149,6 +155,8 @@ bool Canvas::DrawTilePainter(const Bitmap &bitmap, int size, float scale) {
// Draw the currently selected tile on the overworld here // Draw the currently selected tile on the overworld here
// Save the coordinates of the selected tile. // Save the coordinates of the selected tile.
drawn_tile_pos_ = io.MousePos; drawn_tile_pos_ = io.MousePos;
SDL_Log("Drawn tile position: %.0f, %.0f", drawn_tile_pos_.x,
drawn_tile_pos_.y);
return true; return true;
} }
@@ -159,13 +167,17 @@ bool Canvas::DrawTilePainter(const Bitmap &bitmap, int size, float scale) {
return false; return false;
} }
bool Canvas::DrawSolidTilePainter(const ImVec4 &color, int size) { bool Canvas::DrawSolidTilePainter(const ImVec4 &color, int tile_size) {
const ImGuiIO &io = ImGui::GetIO(); const ImGuiIO &io = ImGui::GetIO();
const bool is_hovered = ImGui::IsItemHovered(); const bool is_hovered = ImGui::IsItemHovered();
is_hovered_ = is_hovered; is_hovered_ = is_hovered;
// Lock scrolled origin // Lock scrolled origin
const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y); 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); const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
auto scaled_tile_size = tile_size * global_scale_;
static bool is_dragging = false;
static ImVec2 start_drag_pos;
if (is_hovered) { if (is_hovered) {
// Reset the previous tile hover // Reset the previous tile hover
@@ -175,25 +187,36 @@ bool Canvas::DrawSolidTilePainter(const ImVec4 &color, int size) {
// Calculate the coordinates of the mouse // Calculate the coordinates of the mouse
ImVec2 painter_pos; ImVec2 painter_pos;
painter_pos.x = std::floor((double)mouse_pos.x / size) * size; painter_pos.x =
painter_pos.y = std::floor((double)mouse_pos.y / size) * size; std::floor((double)mouse_pos.x / scaled_tile_size) * scaled_tile_size;
painter_pos.y =
std::floor((double)mouse_pos.y / scaled_tile_size) * scaled_tile_size;
// Clamp the size to a grid // Clamp the size to a grid
painter_pos.x = std::clamp(painter_pos.x, 0.0f, canvas_sz_.x); painter_pos.x =
painter_pos.y = std::clamp(painter_pos.y, 0.0f, canvas_sz_.y); std::clamp(painter_pos.x, 0.0f, canvas_sz_.x * global_scale_);
painter_pos.y =
std::clamp(painter_pos.y, 0.0f, canvas_sz_.y * global_scale_);
auto painter_pos_end = ImVec2(painter_pos.x + size, painter_pos.y + size); auto painter_pos_end = ImVec2(painter_pos.x + scaled_tile_size,
painter_pos.y + scaled_tile_size);
points_.push_back(painter_pos); points_.push_back(painter_pos);
points_.push_back(painter_pos_end); points_.push_back(painter_pos_end);
draw_list_->AddRectFilled( draw_list_->AddRectFilled(
ImVec2(origin.x + painter_pos.x, origin.y + painter_pos.y), ImVec2(origin.x + painter_pos.x + 1, origin.y + painter_pos.y + 1),
ImVec2(origin.x + painter_pos.x + size, ImVec2(origin.x + painter_pos.x + scaled_tile_size,
origin.y + painter_pos.y + size), origin.y + painter_pos.y + scaled_tile_size),
IM_COL32(color.x * 255, color.y * 255, color.z * 255, 255)); IM_COL32(color.x * 255, color.y * 255, color.z * 255, 255));
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
drawn_tile_pos_ = mouse_pos; is_dragging = true;
start_drag_pos = painter_pos;
}
if (is_dragging && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
is_dragging = false;
drawn_tile_pos_ = start_drag_pos;
return true; return true;
} }
@@ -204,13 +227,12 @@ bool Canvas::DrawSolidTilePainter(const ImVec4 &color, int size) {
return false; return false;
} }
void Canvas::DrawTileOnBitmap(const ImVec2 &position, int tile_size, void Canvas::DrawTileOnBitmap(int tile_size, gfx::Bitmap &bitmap,
gfx::Bitmap &bitmap, uint16_t color) { ImVec4 color) {
// Calculate the tile indices based on the click position const ImVec2 position = drawn_tile_pos_;
int tile_index_x = static_cast<int>(position.x) / tile_size; int tile_index_x = static_cast<int>(position.x / global_scale_) / tile_size;
int tile_index_y = static_cast<int>(position.y) / tile_size; int tile_index_y = static_cast<int>(position.y / global_scale_) / tile_size;
// Calculate the pixel start position based on tile index and tile size
ImVec2 start_position(tile_index_x * tile_size, tile_index_y * tile_size); ImVec2 start_position(tile_index_x * tile_size, tile_index_y * tile_size);
// Update the bitmap's pixel data based on the start_position and color // Update the bitmap's pixel data based on the start_position and color
@@ -221,7 +243,7 @@ void Canvas::DrawTileOnBitmap(const ImVec2 &position, int tile_size,
(start_position.y + y) * bitmap.width() + (start_position.x + x); (start_position.y + y) * bitmap.width() + (start_position.x + x);
// Write the color to the pixel // Write the color to the pixel
bitmap.WriteToPixel(pixel_index, color); bitmap.WriteColor(pixel_index, color);
} }
} }
} }
@@ -299,11 +321,11 @@ void Canvas::DrawBitmap(const Bitmap &bitmap, int border_offset, bool ready) {
} }
void Canvas::DrawBitmap(const Bitmap &bitmap, int border_offset, float scale) { void Canvas::DrawBitmap(const Bitmap &bitmap, int border_offset, float scale) {
draw_list_->AddImage( draw_list_->AddImage((void *)bitmap.texture(),
(void *)bitmap.texture(), ImVec2(canvas_p0_.x, canvas_p0_.y),
ImVec2(canvas_p0_.x + border_offset, canvas_p0_.y + border_offset), ImVec2(canvas_p0_.x + (bitmap.width() * scale),
ImVec2(canvas_p0_.x + (bitmap.width() * scale), canvas_p0_.y + (bitmap.height() * scale)));
canvas_p0_.y + (bitmap.height() * scale))); draw_list_->AddRect(canvas_p0_, canvas_p1_, kRectangleBorder);
} }
void Canvas::DrawBitmap(const Bitmap &bitmap, int x_offset, int y_offset, void Canvas::DrawBitmap(const Bitmap &bitmap, int x_offset, int y_offset,
@@ -417,23 +439,23 @@ void Canvas::DrawGrid(float grid_step) {
if (custom_step_ != 0.f) grid_step = custom_step_; if (custom_step_ != 0.f) grid_step = custom_step_;
grid_step *= global_scale_; // Apply global scale to grid step grid_step *= global_scale_; // Apply global scale to grid step
for (float x = fmodf(scrolling_.x, grid_step); x < canvas_sz_.x; for (float x = fmodf(scrolling_.x, grid_step);
x += grid_step) x < canvas_sz_.x * global_scale_; x += grid_step)
draw_list_->AddLine(ImVec2(canvas_p0_.x + x, canvas_p0_.y), draw_list_->AddLine(ImVec2(canvas_p0_.x + x, canvas_p0_.y),
ImVec2(canvas_p0_.x + x, canvas_p1_.y), ImVec2(canvas_p0_.x + x, canvas_p1_.y),
IM_COL32(200, 200, 200, 50), 0.5f); IM_COL32(200, 200, 200, 50), 0.5f);
for (float y = fmodf(scrolling_.y, grid_step); y < canvas_sz_.y; for (float y = fmodf(scrolling_.y, grid_step);
y += grid_step) y < canvas_sz_.y * global_scale_; y += grid_step)
draw_list_->AddLine(ImVec2(canvas_p0_.x, canvas_p0_.y + y), draw_list_->AddLine(ImVec2(canvas_p0_.x, canvas_p0_.y + y),
ImVec2(canvas_p1_.x, canvas_p0_.y + y), ImVec2(canvas_p1_.x, canvas_p0_.y + y),
IM_COL32(200, 200, 200, 50), 0.5f); IM_COL32(200, 200, 200, 50), 0.5f);
if (enable_hex_tile_labels_) { if (enable_hex_tile_labels_) {
// Draw the hex ID of the tile in the center of the tile square // Draw the hex ID of the tile in the center of the tile square
for (float x = fmodf(scrolling_.x, grid_step); x < canvas_sz_.x; for (float x = fmodf(scrolling_.x, grid_step);
x += grid_step) { x < canvas_sz_.x * global_scale_; x += grid_step) {
for (float y = fmodf(scrolling_.y, grid_step); y < canvas_sz_.y; for (float y = fmodf(scrolling_.y, grid_step);
y += grid_step) { y < canvas_sz_.y * global_scale_; y += grid_step) {
int tile_x = (x - scrolling_.x) / grid_step; int tile_x = (x - scrolling_.x) / grid_step;
int tile_y = (y - scrolling_.y) / grid_step; int tile_y = (y - scrolling_.y) / grid_step;
int tile_id = tile_x + (tile_y * 16); int tile_id = tile_x + (tile_y * 16);

View File

@@ -48,8 +48,7 @@ class Canvas {
bool DrawSolidTilePainter(const ImVec4& color, int size); bool DrawSolidTilePainter(const ImVec4& color, int size);
// Draws a tile on the canvas at the specified position // Draws a tile on the canvas at the specified position
void DrawTileOnBitmap(const ImVec2& position, int tile_size, void DrawTileOnBitmap(int tile_size, gfx::Bitmap& bitmap, ImVec4 color);
gfx::Bitmap& bitmap, uint16_t color);
// Dictates which tile is currently selected based on what the user clicks // Dictates which tile is currently selected based on what the user clicks
// in the canvas window. Represented and split apart into a grid of tiles. // in the canvas window. Represented and split apart into a grid of tiles.
@@ -81,15 +80,17 @@ class Canvas {
auto Points() const { return points_; } auto Points() const { return points_; }
auto GetDrawList() const { return draw_list_; } auto GetDrawList() const { return draw_list_; }
auto GetZeroPoint() const { return canvas_p0_; } auto zero_point() const { return canvas_p0_; }
auto Scrolling() const { return scrolling_; } auto Scrolling() const { return scrolling_; }
auto drawn_tile_position() const { return drawn_tile_pos_; } auto drawn_tile_position() const { return drawn_tile_pos_; }
auto GetCanvasSize() const { return canvas_sz_; } auto canvas_size() const { return canvas_sz_; }
void SetCanvasSize(ImVec2 canvas_size) { void SetCanvasSize(ImVec2 canvas_size) {
canvas_sz_ = canvas_size; canvas_sz_ = canvas_size;
custom_canvas_size_ = true; custom_canvas_size_ = true;
} }
auto IsMouseHovering() const { return is_hovered_; } auto IsMouseHovering() const { return is_hovered_; }
void ZoomIn() { global_scale_ += 0.1f; }
void ZoomOut() { global_scale_ -= 0.1f; }
void set_global_scale(float scale) { global_scale_ = scale; } void set_global_scale(float scale) { global_scale_ = scale; }
auto global_scale() const { return global_scale_; } auto global_scale() const { return global_scale_; }

View File

@@ -23,18 +23,19 @@ namespace gui {
void SelectablePalettePipeline(uint64_t& palette_id, bool& refresh_graphics, void SelectablePalettePipeline(uint64_t& palette_id, bool& refresh_graphics,
gfx::SNESPalette& palette) { gfx::SNESPalette& palette) {
const auto palette_row_size = 7;
if (ImGuiID child_id = ImGui::GetID((void*)(intptr_t)100); if (ImGuiID child_id = ImGui::GetID((void*)(intptr_t)100);
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true, ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) { ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
ImGui::BeginGroup(); // Lock X position ImGui::BeginGroup(); // Lock X position
ImGui::Text("Palette"); ImGui::Text("Palette");
for (int n = 0; n < palette.size(); n++) { for (int n = 0; n < palette.size(); n++) {
ImGui::PushID(n); ImGui::PushID(n);
if ((n % 7) != 0) ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y); if ((n % palette_row_size) != 0)
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
// Check if the current row is selected // Check if the current row is selected
bool is_selected = (palette_id == n / 7); bool is_selected = (palette_id == n / palette_row_size);
// Add outline rectangle to the selected row // Add outline rectangle to the selected row
if (is_selected) { if (is_selected) {
@@ -47,7 +48,7 @@ void SelectablePalettePipeline(uint64_t& palette_id, bool& refresh_graphics,
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoPicker |
ImGuiColorEditFlags_NoTooltip, ImGuiColorEditFlags_NoTooltip,
ImVec2(20, 20))) { ImVec2(20, 20))) {
palette_id = n / 7; palette_id = n / palette_row_size;
refresh_graphics = true; refresh_graphics = true;
} }
@@ -76,15 +77,15 @@ void GraphicsBinCanvasPipeline(int width, int height, int tile_size,
if (is_loaded) { if (is_loaded) {
for (const auto& [key, value] : graphics_bin) { for (const auto& [key, value] : graphics_bin) {
int offset = height * (key + 1); int offset = height * (key + 1);
int top_left_y = canvas.GetZeroPoint().y + 2; int top_left_y = canvas.zero_point().y + 2;
if (key >= 1) { if (key >= 1) {
top_left_y = canvas.GetZeroPoint().y + height * key; top_left_y = canvas.zero_point().y + height * key;
} }
canvas.GetDrawList()->AddImage( canvas.GetDrawList()->AddImage(
(void*)value.texture(), (void*)value.texture(),
ImVec2(canvas.GetZeroPoint().x + 2, top_left_y), ImVec2(canvas.zero_point().x + 2, top_left_y),
ImVec2(canvas.GetZeroPoint().x + 0x100, ImVec2(canvas.zero_point().x + 0x100,
canvas.GetZeroPoint().y + offset)); canvas.zero_point().y + offset));
} }
} }
canvas.DrawTileSelector(tile_size); canvas.DrawTileSelector(tile_size);
@@ -108,15 +109,15 @@ void GraphicsManagerCanvasPipeline(int width, int height, int tile_size,
if (is_loaded) { if (is_loaded) {
for (const auto& [key, value] : graphics_manager) { for (const auto& [key, value] : graphics_manager) {
int offset = height * (key + 1); int offset = height * (key + 1);
int top_left_y = canvas.GetZeroPoint().y + 2; int top_left_y = canvas.zero_point().y + 2;
if (key >= 1) { if (key >= 1) {
top_left_y = canvas.GetZeroPoint().y + height * key; top_left_y = canvas.zero_point().y + height * key;
} }
canvas.GetDrawList()->AddImage( canvas.GetDrawList()->AddImage(
(void*)value->texture(), (void*)value->texture(),
ImVec2(canvas.GetZeroPoint().x + 2, top_left_y), ImVec2(canvas.zero_point().x + 2, top_left_y),
ImVec2(canvas.GetZeroPoint().x + 0x100, ImVec2(canvas.zero_point().x + 0x100,
canvas.GetZeroPoint().y + offset)); canvas.zero_point().y + offset));
} }
} }
canvas.DrawTileSelector(tile_size); canvas.DrawTileSelector(tile_size);