From d1b4ac24d2b4d948f34fd2f392f28d36340c53e3 Mon Sep 17 00:00:00 2001 From: fleroviux Date: Sat, 15 Jul 2023 23:35:10 +0200 Subject: [PATCH 01/13] Qt: begin work on a sprite viewer window --- src/nba/include/nba/core.hpp | 1 + src/nba/src/core.cpp | 4 + src/nba/src/core.hpp | 1 + src/nba/src/hw/ppu/ppu.hpp | 4 + src/platform/qt/CMakeLists.txt | 2 + src/platform/qt/src/widget/main_window.cpp | 9 + src/platform/qt/src/widget/main_window.hpp | 2 + src/platform/qt/src/widget/sprite_viewer.cpp | 286 +++++++++++++++++++ src/platform/qt/src/widget/sprite_viewer.hpp | 60 ++++ 9 files changed, 369 insertions(+) create mode 100644 src/platform/qt/src/widget/sprite_viewer.cpp create mode 100644 src/platform/qt/src/widget/sprite_viewer.hpp diff --git a/src/nba/include/nba/core.hpp b/src/nba/include/nba/core.hpp index b1606bdbc..8ccbd385a 100644 --- a/src/nba/include/nba/core.hpp +++ b/src/nba/include/nba/core.hpp @@ -36,6 +36,7 @@ struct CoreBase { virtual auto GetROM() -> ROM& = 0; virtual auto GetPRAM() -> u8* = 0; virtual auto GetVRAM() -> u8* = 0; + virtual auto GetOAM() -> u8* = 0; // @todo: come up with a solution for reading write-only registers. virtual auto PeekByteIO(u32 address) -> u8 = 0; virtual auto PeekHalfIO(u32 address) -> u16 = 0; diff --git a/src/nba/src/core.cpp b/src/nba/src/core.cpp index 85843f799..c7583d8ae 100644 --- a/src/nba/src/core.cpp +++ b/src/nba/src/core.cpp @@ -159,6 +159,10 @@ auto Core::GetVRAM() -> u8* { return ppu.GetVRAM(); } +auto Core::GetOAM() -> u8* { + return ppu.GetOAM(); +} + auto Core::PeekByteIO(u32 address) -> u8 { return bus.hw.ReadByte(address); } diff --git a/src/nba/src/core.hpp b/src/nba/src/core.hpp index 82190c0ef..cdc169561 100644 --- a/src/nba/src/core.hpp +++ b/src/nba/src/core.hpp @@ -35,6 +35,7 @@ struct Core final : CoreBase { auto GetROM() -> ROM& override; auto GetPRAM() -> u8* override; auto GetVRAM() -> u8* override; + auto GetOAM() -> u8* override; auto PeekByteIO(u32 address) -> u8 override; auto PeekHalfIO(u32 address) -> u16 override; auto PeekWordIO(u32 address) -> u32 override; diff --git a/src/nba/src/hw/ppu/ppu.hpp b/src/nba/src/hw/ppu/ppu.hpp index 0cd1690fd..88074b9cb 100644 --- a/src/nba/src/hw/ppu/ppu.hpp +++ b/src/nba/src/hw/ppu/ppu.hpp @@ -43,6 +43,10 @@ struct PPU { return vram; } + auto GetOAM() -> u8* { + return oam; + } + template auto ALWAYS_INLINE ReadPRAM(u32 address) noexcept -> T { return read(pram, address & 0x3FF); diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index c430bdee8..bbfcfc8f3 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -12,6 +12,7 @@ set(SOURCES src/widget/screen.cpp src/widget/palette_box.cpp src/widget/palette_viewer_window.cpp + src/widget/sprite_viewer.cpp src/config.cpp src/main.cpp ) @@ -25,6 +26,7 @@ set(HEADERS src/widget/palette_box.hpp src/widget/palette_viewer_window.hpp src/widget/screen.hpp + src/widget/sprite_viewer.hpp src/config.hpp ) diff --git a/src/platform/qt/src/widget/main_window.cpp b/src/platform/qt/src/widget/main_window.cpp index 90b2df5d6..9336e6e3e 100644 --- a/src/platform/qt/src/widget/main_window.cpp +++ b/src/platform/qt/src/widget/main_window.cpp @@ -380,6 +380,15 @@ void MainWindow::CreateToolsMenu() { background_viewer_window->show(); }); + + connect(tools_menu->addAction(tr("Sprite Viewer")), &QAction::triggered, [this]() { + if(!sprite_viewer_window) { + sprite_viewer_window = new SpriteViewer{core.get()}; + connect(screen.get(), &Screen::RequestDraw, sprite_viewer_window, &SpriteViewer::Update); + } + + sprite_viewer_window->show(); + }); } void MainWindow::CreateHelpMenu() { diff --git a/src/platform/qt/src/widget/main_window.hpp b/src/platform/qt/src/widget/main_window.hpp index a26de9044..493427303 100644 --- a/src/platform/qt/src/widget/main_window.hpp +++ b/src/platform/qt/src/widget/main_window.hpp @@ -27,6 +27,7 @@ #include "widget/input_window.hpp" #include "widget/palette_viewer_window.hpp" #include "widget/screen.hpp" +#include "widget/sprite_viewer.hpp" #include "config.hpp" struct MainWindow : QMainWindow { @@ -152,6 +153,7 @@ private slots: PaletteViewerWindow* palette_viewer_window; BackgroundViewerWindow* background_viewer_window; + SpriteViewer* sprite_viewer_window; Q_OBJECT }; diff --git a/src/platform/qt/src/widget/sprite_viewer.cpp b/src/platform/qt/src/widget/sprite_viewer.cpp new file mode 100644 index 000000000..29baecd05 --- /dev/null +++ b/src/platform/qt/src/widget/sprite_viewer.cpp @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2023 fleroviux + * + * Licensed under GPLv3 or any later version. + * Refer to the included LICENSE file. + */ + +#include +#include +#include + +#include +#include +#include + +#include "sprite_viewer.hpp" + +// -------------------------------------------------------------------- + +// @todo: deduplicate these helpers across all viewers + +static u32 RGB555(u16 rgb555) { + const uint r = (rgb555 >> 0) & 31U; + const uint g = (rgb555 >> 5) & 31U; + const uint b = (rgb555 >> 10) & 31U; + + return 0xFF000000 | (r << 3 | r >> 2) << 16 | (g << 3 | g >> 2) << 8 | (b << 3 | b >> 2); +} + +static const auto CreateMonospaceLabel = [](QLabel*& label) { + label = new QLabel{"-"}; + label->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + label->setTextInteractionFlags(Qt::TextSelectableByMouse); + return label; +}; + +static const auto CreateCheckBox = [](QCheckBox*& check_box) { + check_box = new QCheckBox{}; + check_box->setEnabled(false); + return check_box; +}; + +// -------------------------------------------------------------------- + + +SpriteViewer::SpriteViewer(nba::CoreBase* core, QWidget* parent) : QWidget{parent}, core{core} { + pram = (u16*)core->GetPRAM(); + vram = core->GetVRAM(); + oam = core->GetOAM(); + + QVBoxLayout* layout = new QVBoxLayout{}; + + sprite_index_input = new QSpinBox{}; + sprite_index_input->setMinimum(0); + sprite_index_input->setMaximum(127); + layout->addWidget(sprite_index_input); + + magnification_input = new QSpinBox{}; + magnification_input->setMinimum(1); + magnification_input->setMaximum(16); + layout->addWidget(magnification_input); + + canvas = new QWidget{}; + canvas->installEventFilter(this); + layout->addWidget(canvas); + + { + QGroupBox* box = new QGroupBox{}; + QGridLayout* grid = new QGridLayout{}; + + int row = 0; + + const auto PushRow = [&](const QString& label, QWidget* widget) { + grid->addWidget(new QLabel{QStringLiteral("%1:").arg(label)}, row, 0); + grid->addWidget(widget, row++, 1); + }; + + PushRow("Enabled", CreateCheckBox(check_sprite_enabled)); + PushRow("Position", CreateMonospaceLabel(label_sprite_position)); + PushRow("Size", CreateMonospaceLabel(label_sprite_size)); + PushRow("Tile number", CreateMonospaceLabel(label_sprite_tile_number)); + PushRow("Palette", CreateMonospaceLabel(label_sprite_palette)); + PushRow("8BPP", CreateCheckBox(check_sprite_8bpp)); + PushRow("Flip V", CreateCheckBox(check_sprite_vflip)); + PushRow("Flip H", CreateCheckBox(check_sprite_hflip)); + PushRow("Mode", CreateMonospaceLabel(label_sprite_mode)); + PushRow("Affine", CreateCheckBox(check_sprite_affine)); + PushRow("Transform #", CreateMonospaceLabel(label_sprite_transform)); + PushRow("Double-size", CreateCheckBox(check_sprite_double_size)); + PushRow("Mosaic", CreateCheckBox(check_sprite_mosaic)); + PushRow("Render cycles", CreateMonospaceLabel(label_sprite_render_cycles)); + + // // Temporary workaround: + // grid->setColumnStretch(2, 100); + // grid->setRowStretch(row, 100); + + box->setLayout(grid); + layout->addWidget(box); + } + + setLayout(layout); + + // @todo: move this out of here + setWindowTitle("Sprite Viewer"); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); +} + +void SpriteViewer::Update() { + const int offset = sprite_index_input->value() << 3; + + const u16 attr0 = nba::read(oam, offset); + const u16 attr1 = nba::read(oam, offset + 2); + const u16 attr2 = nba::read(oam, offset + 4); + + const int shape = attr0 >> 14; + const int size = attr1 >> 14; + + static constexpr int k_sprite_size[4][4][2] = { + { { 8 , 8 }, { 16, 16 }, { 32, 32 }, { 64, 64 } }, // Square + { { 16, 8 }, { 32, 8 }, { 32, 16 }, { 64, 32 } }, // Horizontal + { { 8 , 16 }, { 8 , 32 }, { 16, 32 }, { 32, 64 } }, // Vertical + { { 8 , 8 }, { 8 , 8 }, { 8 , 8 }, { 8 , 8 } } // Prohibited + }; + + const int width = k_sprite_size[shape][size][0]; + const int height = k_sprite_size[shape][size][1]; + + const bool is_8bpp = attr0 & (1 << 13); + const uint tile_number = attr2 & 0x3FFu; + + const bool one_dimensional_mapping = core->PeekHalfIO(0x04000000) & (1 << 6); + + const int tiles_x = width >> 3; + const int tiles_y = height >> 3; + + u32* const buffer = (u32*)image_rgb32.bits(); + + if(is_8bpp) { + const u16* palette = &pram[256]; + + for(int tile_y = 0; tile_y < tiles_y; tile_y++) { + for(int tile_x = 0; tile_x < tiles_x; tile_x++) { + int current_tile_number; + + if(one_dimensional_mapping) { + current_tile_number = (tile_number + tile_y * (width >> 2) + (tile_x << 1)) & 0x3FF; + } else { + current_tile_number = ((tile_number + (tile_y * 32)) & 0x3E0) | (((tile_number & ~1) + (tile_x << 1)) & 0x1F); + } + + // @todo: handle bad tile numbers and overflows and the likes + + u32 tile_address = 0x10000u + (current_tile_number << 5); + + for(int y = 0; y < 8; y++) { + u64 tile_data = nba::read(vram, tile_address); + + u32* dst = &buffer[tile_y << 9 | y << 6 | tile_x << 3]; + + for(int x = 0; x < 8; x++) { + *dst++ = RGB555(palette[tile_data & 255u]); + tile_data >>= 8; + } + + tile_address += sizeof(u64); + } + } + } + } else { + const u16* palette = &pram[256 | attr2 >> 12 << 4]; + + for(int tile_y = 0; tile_y < tiles_y; tile_y++) { + for(int tile_x = 0; tile_x < tiles_x; tile_x++) { + int current_tile_number; + + if(one_dimensional_mapping) { + current_tile_number = (tile_number + tile_y * (width >> 3) + tile_x) & 0x3FF; + } else { + current_tile_number = ((tile_number + (tile_y * 32)) & 0x3E0) | ((tile_number + tile_x) & 0x1F); + } + + // @todo: handle bad tile numbers and overflows and the likes + + u32 tile_address = 0x10000u + (current_tile_number << 5); + + for(int y = 0; y < 8; y++) { + u32 tile_data = nba::read(vram, tile_address); + + u32* dst = &buffer[tile_y << 9 | y << 6 | tile_x << 3]; + + for(int x = 0; x < 8; x++) { + *dst++ = RGB555(palette[tile_data & 15u]); + tile_data >>= 4; + } + + tile_address += sizeof(u32); + } + } + } + } + + static constexpr const char* k_mode_names[4] { + "Normal", "Semi-Transparent", "Window", "Prohibited" + }; + + const uint x = attr1 & 0x1FFu; + const uint y = attr0 & 0x0FFu; + const bool affine = attr0 & (1 << 8); + const uint palette = is_8bpp ? 0u : (attr2 >> 12); + const uint mode = (attr0 >> 10) & 3; + const bool mosaic = attr0 & (1 << 12); + + label_sprite_position->setText(QStringLiteral("%1, %2").arg(x).arg(y)); + label_sprite_size->setText(QStringLiteral("%1, %2").arg(width).arg(height)); + label_sprite_tile_number->setText(QStringLiteral("%1").arg(tile_number)); + label_sprite_palette->setText(QStringLiteral("%1").arg(palette)); + check_sprite_8bpp->setChecked(is_8bpp); + label_sprite_mode->setText(k_mode_names[mode]); + check_sprite_affine->setChecked(affine); + check_sprite_mosaic->setChecked(mosaic); + + check_sprite_vflip->setEnabled(!affine); + check_sprite_hflip->setEnabled(!affine); + check_sprite_double_size->setEnabled(affine); + + const int signed_x = x >= 240 ? ((int)x - 512) : (int)x; + + int render_cycles = 0; + + if(affine) { + const bool double_size = attr0 & (1 << 9); + const uint transform = (attr1 >> 9) & 31u; + + check_sprite_enabled->setChecked(true); + check_sprite_vflip->setChecked(false); + check_sprite_hflip->setChecked(false); + label_sprite_transform->setText(QStringLiteral("%1").arg(transform)); + check_sprite_double_size->setChecked(double_size); + label_sprite_render_cycles->setText("0 (0%)"); + + const int clipped_draw_width = std::max(0, (double_size ? 2 * width : width) + std::min(signed_x, 0)); + + render_cycles = 10 + 2 * clipped_draw_width; + } else { + const bool enabled = !(attr0 & (1 << 9)); + const bool hflip = attr1 & (1 << 12); + const bool vflip = attr1 & (1 << 13); + + check_sprite_enabled->setChecked(enabled); + check_sprite_vflip->setChecked(vflip); + check_sprite_hflip->setChecked(hflip); + label_sprite_transform->setText("n/a"); + check_sprite_double_size->setChecked(false); + label_sprite_render_cycles->setText("0 (0%)"); + + if(enabled) { + render_cycles = std::max(0, width + std::min(signed_x, 0)); + } + } + + const int available_render_cycles = (core->PeekHalfIO(0x04000000) & (1 << 5)) ? 964 : 1232; + + label_sprite_render_cycles->setText(QString::fromStdString(fmt::format("{} ({:.2f} %)", render_cycles, 100.0f * (float)render_cycles / available_render_cycles))); + + sprite_width = width; + sprite_height = height; + + const int magnification = magnification_input->value(); + magnified_sprite_width = width * magnification; + magnified_sprite_height = height * magnification; + canvas->resize(magnified_sprite_width, magnified_sprite_height); + canvas->update(); +} + +bool SpriteViewer::eventFilter(QObject* object, QEvent* event) { + if(object == canvas && event->type() == QEvent::Paint) { + const QRect src_rect{0, 0, sprite_width, sprite_height}; + const QRect dst_rect{0, 0, magnified_sprite_width, magnified_sprite_height}; + + QPainter painter{canvas}; + painter.drawImage(dst_rect, image_rgb32, src_rect); + return true; + } + + return false; +} \ No newline at end of file diff --git a/src/platform/qt/src/widget/sprite_viewer.hpp b/src/platform/qt/src/widget/sprite_viewer.hpp new file mode 100644 index 000000000..023c5c35f --- /dev/null +++ b/src/platform/qt/src/widget/sprite_viewer.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2023 fleroviux + * + * Licensed under GPLv3 or any later version. + * Refer to the included LICENSE file. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct SpriteViewer : QWidget { + SpriteViewer(nba::CoreBase* core, QWidget* parent = nullptr); + + bool eventFilter(QObject* object, QEvent* event) override; + +public slots: + void Update(); + +private: + nba::CoreBase* core; + u16* pram; + u8* vram; + u8* oam; + + QImage image_rgb32{64, 64, QImage::Format_RGB32}; + QSpinBox* sprite_index_input; + QSpinBox* magnification_input; + QWidget* canvas; + + QCheckBox* check_sprite_enabled; + QLabel* label_sprite_position; + QLabel* label_sprite_size; + QLabel* label_sprite_tile_number; + QLabel* label_sprite_palette; + QCheckBox* check_sprite_8bpp; + QCheckBox* check_sprite_vflip; + QCheckBox* check_sprite_hflip; + QLabel* label_sprite_mode; + QCheckBox* check_sprite_affine; + QLabel* label_sprite_transform; + QCheckBox* check_sprite_double_size; + QCheckBox* check_sprite_mosaic; + QLabel* label_sprite_render_cycles; + + int sprite_width = 0; + int sprite_height = 0; + int magnified_sprite_width = 0; + int magnified_sprite_height = 0; + + Q_OBJECT +}; From 53bf25c2e18ddb12dac1c20d690f126e8c8e797c Mon Sep 17 00:00:00 2001 From: fleroviux Date: Sat, 15 Jul 2023 23:50:36 +0200 Subject: [PATCH 02/13] Slight cleanup --- src/platform/qt/src/widget/sprite_viewer.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/platform/qt/src/widget/sprite_viewer.cpp b/src/platform/qt/src/widget/sprite_viewer.cpp index 29baecd05..92d31caa4 100644 --- a/src/platform/qt/src/widget/sprite_viewer.cpp +++ b/src/platform/qt/src/widget/sprite_viewer.cpp @@ -258,9 +258,11 @@ void SpriteViewer::Update() { } } - const int available_render_cycles = (core->PeekHalfIO(0x04000000) & (1 << 5)) ? 964 : 1232; + const bool fast_hblank_oam_access = core->PeekHalfIO(0x04000000) & (1 << 5); + const int available_render_cycles = fast_hblank_oam_access ? 964 : 1232; + const float render_cycle_percentage = 100.0f * (float)render_cycles / (float)available_render_cycles; - label_sprite_render_cycles->setText(QString::fromStdString(fmt::format("{} ({:.2f} %)", render_cycles, 100.0f * (float)render_cycles / available_render_cycles))); + label_sprite_render_cycles->setText(QString::fromStdString(fmt::format("{} ({:.2f} %)", render_cycles, render_cycle_percentage))); sprite_width = width; sprite_height = height; From b23744c734b675c9288d29034c80c14d38354e8c Mon Sep 17 00:00:00 2001 From: fleroviux Date: Sun, 16 Jul 2023 00:10:47 +0200 Subject: [PATCH 03/13] Attempt to make sprite bitmap rendering code more reusable --- src/platform/qt/src/widget/sprite_viewer.cpp | 169 +++++++++++-------- src/platform/qt/src/widget/sprite_viewer.hpp | 2 + 2 files changed, 99 insertions(+), 72 deletions(-) diff --git a/src/platform/qt/src/widget/sprite_viewer.cpp b/src/platform/qt/src/widget/sprite_viewer.cpp index 92d31caa4..c4c5c80c3 100644 --- a/src/platform/qt/src/widget/sprite_viewer.cpp +++ b/src/platform/qt/src/widget/sprite_viewer.cpp @@ -106,7 +106,11 @@ SpriteViewer::SpriteViewer(nba::CoreBase* core, QWidget* parent) : QWidget{paren } void SpriteViewer::Update() { - const int offset = sprite_index_input->value() << 3; + const int index = sprite_index_input->value(); + + RenderSprite(index, (u32*)image_rgb32.bits()); + + const int offset = index << 3; const u16 attr0 = nba::read(oam, offset); const u16 attr1 = nba::read(oam, offset + 2); @@ -128,77 +132,6 @@ void SpriteViewer::Update() { const bool is_8bpp = attr0 & (1 << 13); const uint tile_number = attr2 & 0x3FFu; - const bool one_dimensional_mapping = core->PeekHalfIO(0x04000000) & (1 << 6); - - const int tiles_x = width >> 3; - const int tiles_y = height >> 3; - - u32* const buffer = (u32*)image_rgb32.bits(); - - if(is_8bpp) { - const u16* palette = &pram[256]; - - for(int tile_y = 0; tile_y < tiles_y; tile_y++) { - for(int tile_x = 0; tile_x < tiles_x; tile_x++) { - int current_tile_number; - - if(one_dimensional_mapping) { - current_tile_number = (tile_number + tile_y * (width >> 2) + (tile_x << 1)) & 0x3FF; - } else { - current_tile_number = ((tile_number + (tile_y * 32)) & 0x3E0) | (((tile_number & ~1) + (tile_x << 1)) & 0x1F); - } - - // @todo: handle bad tile numbers and overflows and the likes - - u32 tile_address = 0x10000u + (current_tile_number << 5); - - for(int y = 0; y < 8; y++) { - u64 tile_data = nba::read(vram, tile_address); - - u32* dst = &buffer[tile_y << 9 | y << 6 | tile_x << 3]; - - for(int x = 0; x < 8; x++) { - *dst++ = RGB555(palette[tile_data & 255u]); - tile_data >>= 8; - } - - tile_address += sizeof(u64); - } - } - } - } else { - const u16* palette = &pram[256 | attr2 >> 12 << 4]; - - for(int tile_y = 0; tile_y < tiles_y; tile_y++) { - for(int tile_x = 0; tile_x < tiles_x; tile_x++) { - int current_tile_number; - - if(one_dimensional_mapping) { - current_tile_number = (tile_number + tile_y * (width >> 3) + tile_x) & 0x3FF; - } else { - current_tile_number = ((tile_number + (tile_y * 32)) & 0x3E0) | ((tile_number + tile_x) & 0x1F); - } - - // @todo: handle bad tile numbers and overflows and the likes - - u32 tile_address = 0x10000u + (current_tile_number << 5); - - for(int y = 0; y < 8; y++) { - u32 tile_data = nba::read(vram, tile_address); - - u32* dst = &buffer[tile_y << 9 | y << 6 | tile_x << 3]; - - for(int x = 0; x < 8; x++) { - *dst++ = RGB555(palette[tile_data & 15u]); - tile_data >>= 4; - } - - tile_address += sizeof(u32); - } - } - } - } - static constexpr const char* k_mode_names[4] { "Normal", "Semi-Transparent", "Window", "Prohibited" }; @@ -274,6 +207,98 @@ void SpriteViewer::Update() { canvas->update(); } +void SpriteViewer::RenderSprite(int index, u32* buffer) { + static constexpr int k_sprite_size[4][4][2] = { + { { 8 , 8 }, { 16, 16 }, { 32, 32 }, { 64, 64 } }, // Square + { { 16, 8 }, { 32, 8 }, { 32, 16 }, { 64, 32 } }, // Horizontal + { { 8 , 16 }, { 8 , 32 }, { 16, 32 }, { 32, 64 } }, // Vertical + { { 8 , 8 }, { 8 , 8 }, { 8 , 8 }, { 8 , 8 } } // Prohibited + }; + + const int offset = index << 3; + + const u16 attr0 = nba::read(oam, offset); + const u16 attr1 = nba::read(oam, offset + 2); + const u16 attr2 = nba::read(oam, offset + 4); + + const int shape = attr0 >> 14; + const int size = attr1 >> 14; + const int width = k_sprite_size[shape][size][0]; + const int height = k_sprite_size[shape][size][1]; + + const bool is_8bpp = attr0 & (1 << 13); + const uint tile_number = attr2 & 0x3FFu; + + const bool one_dimensional_mapping = core->PeekHalfIO(0x04000000) & (1 << 6); + + const int tiles_x = width >> 3; + const int tiles_y = height >> 3; + + if(is_8bpp) { + const u16* palette = &pram[256]; + + for(int tile_y = 0; tile_y < tiles_y; tile_y++) { + for(int tile_x = 0; tile_x < tiles_x; tile_x++) { + int current_tile_number; + + if(one_dimensional_mapping) { + current_tile_number = (tile_number + tile_y * (width >> 2) + (tile_x << 1)) & 0x3FF; + } else { + current_tile_number = ((tile_number + (tile_y * 32)) & 0x3E0) | (((tile_number & ~1) + (tile_x << 1)) & 0x1F); + } + + // @todo: handle bad tile numbers and overflows and the likes + + u32 tile_address = 0x10000u + (current_tile_number << 5); + + for(int y = 0; y < 8; y++) { + u64 tile_data = nba::read(vram, tile_address); + + u32* dst = &buffer[tile_y << 9 | y << 6 | tile_x << 3]; + + for(int x = 0; x < 8; x++) { + *dst++ = RGB555(palette[tile_data & 255u]); + tile_data >>= 8; + } + + tile_address += sizeof(u64); + } + } + } + } else { + const u16* palette = &pram[256 | attr2 >> 12 << 4]; + + for(int tile_y = 0; tile_y < tiles_y; tile_y++) { + for(int tile_x = 0; tile_x < tiles_x; tile_x++) { + int current_tile_number; + + if(one_dimensional_mapping) { + current_tile_number = (tile_number + tile_y * (width >> 3) + tile_x) & 0x3FF; + } else { + current_tile_number = ((tile_number + (tile_y * 32)) & 0x3E0) | ((tile_number + tile_x) & 0x1F); + } + + // @todo: handle bad tile numbers and overflows and the likes + + u32 tile_address = 0x10000u + (current_tile_number << 5); + + for(int y = 0; y < 8; y++) { + u32 tile_data = nba::read(vram, tile_address); + + u32* dst = &buffer[tile_y << 9 | y << 6 | tile_x << 3]; + + for(int x = 0; x < 8; x++) { + *dst++ = RGB555(palette[tile_data & 15u]); + tile_data >>= 4; + } + + tile_address += sizeof(u32); + } + } + } + } +} + bool SpriteViewer::eventFilter(QObject* object, QEvent* event) { if(object == canvas && event->type() == QEvent::Paint) { const QRect src_rect{0, 0, sprite_width, sprite_height}; diff --git a/src/platform/qt/src/widget/sprite_viewer.hpp b/src/platform/qt/src/widget/sprite_viewer.hpp index 023c5c35f..0028a1fac 100644 --- a/src/platform/qt/src/widget/sprite_viewer.hpp +++ b/src/platform/qt/src/widget/sprite_viewer.hpp @@ -26,6 +26,8 @@ public slots: void Update(); private: + void RenderSprite(int index, u32* buffer); + nba::CoreBase* core; u16* pram; u8* vram; From 7ac7aafa0607076fe2d5b498f19c34c1ae94a0b7 Mon Sep 17 00:00:00 2001 From: fleroviux Date: Sat, 29 Jul 2023 12:43:28 +0200 Subject: [PATCH 04/13] Render sprite atlas test --- src/platform/qt/src/widget/sprite_viewer.cpp | 35 +++++++++++++++++--- src/platform/qt/src/widget/sprite_viewer.hpp | 6 +++- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/platform/qt/src/widget/sprite_viewer.cpp b/src/platform/qt/src/widget/sprite_viewer.cpp index c4c5c80c3..9e6dc5106 100644 --- a/src/platform/qt/src/widget/sprite_viewer.cpp +++ b/src/platform/qt/src/widget/sprite_viewer.cpp @@ -98,6 +98,11 @@ SpriteViewer::SpriteViewer(nba::CoreBase* core, QWidget* parent) : QWidget{paren layout->addWidget(box); } + atlas_canvas = new QWidget{}; + atlas_canvas->setFixedSize(1024, 512); + layout->addWidget(atlas_canvas); + atlas_canvas->installEventFilter(this); + setLayout(layout); // @todo: move this out of here @@ -108,7 +113,9 @@ SpriteViewer::SpriteViewer(nba::CoreBase* core, QWidget* parent) : QWidget{paren void SpriteViewer::Update() { const int index = sprite_index_input->value(); - RenderSprite(index, (u32*)image_rgb32.bits()); + RenderSpriteAtlas(); + + RenderSprite(index, (u32*)image_rgb32.bits(), 64); const int offset = index << 3; @@ -207,7 +214,7 @@ void SpriteViewer::Update() { canvas->update(); } -void SpriteViewer::RenderSprite(int index, u32* buffer) { +void SpriteViewer::RenderSprite(int index, u32* buffer, int stride) { static constexpr int k_sprite_size[4][4][2] = { { { 8 , 8 }, { 16, 16 }, { 32, 32 }, { 64, 64 } }, // Square { { 16, 8 }, { 32, 8 }, { 32, 16 }, { 64, 32 } }, // Horizontal @@ -254,7 +261,7 @@ void SpriteViewer::RenderSprite(int index, u32* buffer) { for(int y = 0; y < 8; y++) { u64 tile_data = nba::read(vram, tile_address); - u32* dst = &buffer[tile_y << 9 | y << 6 | tile_x << 3]; + u32* dst = &buffer[((tile_y << 3) * stride) | (y * stride) | tile_x << 3]; for(int x = 0; x < 8; x++) { *dst++ = RGB555(palette[tile_data & 255u]); @@ -285,7 +292,7 @@ void SpriteViewer::RenderSprite(int index, u32* buffer) { for(int y = 0; y < 8; y++) { u32 tile_data = nba::read(vram, tile_address); - u32* dst = &buffer[tile_y << 9 | y << 6 | tile_x << 3]; + u32* dst = &buffer[((tile_y << 3) * stride) | (y * stride) | tile_x << 3]; for(int x = 0; x < 8; x++) { *dst++ = RGB555(palette[tile_data & 15u]); @@ -299,6 +306,20 @@ void SpriteViewer::RenderSprite(int index, u32* buffer) { } } +void SpriteViewer::RenderSpriteAtlas() { + int sprite_index = 0; + + atlas_image_rgb32.fill(0xFF000000u); + + for(int y = 0; y < 8; y++) { + for(int x = 0; x < 16; x++) { + RenderSprite(sprite_index++, &((u32*)atlas_image_rgb32.bits())[y * 65536 + x * 64], 1024); + } + } + + atlas_canvas->update(); +} + bool SpriteViewer::eventFilter(QObject* object, QEvent* event) { if(object == canvas && event->type() == QEvent::Paint) { const QRect src_rect{0, 0, sprite_width, sprite_height}; @@ -309,5 +330,11 @@ bool SpriteViewer::eventFilter(QObject* object, QEvent* event) { return true; } + if(object == atlas_canvas && event->type() == QEvent::Paint) { + QPainter painter{atlas_canvas}; + painter.drawImage(0, 0, atlas_image_rgb32); + return true; + } + return false; } \ No newline at end of file diff --git a/src/platform/qt/src/widget/sprite_viewer.hpp b/src/platform/qt/src/widget/sprite_viewer.hpp index 0028a1fac..645b8ce70 100644 --- a/src/platform/qt/src/widget/sprite_viewer.hpp +++ b/src/platform/qt/src/widget/sprite_viewer.hpp @@ -26,7 +26,8 @@ public slots: void Update(); private: - void RenderSprite(int index, u32* buffer); + void RenderSprite(int index, u32* buffer, int stride); + void RenderSpriteAtlas(); nba::CoreBase* core; u16* pram; @@ -38,6 +39,9 @@ public slots: QSpinBox* magnification_input; QWidget* canvas; + QImage atlas_image_rgb32{1024, 512, QImage::Format_RGB32}; + QWidget* atlas_canvas; + QCheckBox* check_sprite_enabled; QLabel* label_sprite_position; QLabel* label_sprite_size; From e57758ef20814ae14c7fb20618eddfc12869c8bd Mon Sep 17 00:00:00 2001 From: fleroviux Date: Fri, 3 Nov 2023 00:41:19 +0100 Subject: [PATCH 05/13] Qt: allow creating multiple sprite viewer windows. --- src/platform/qt/CMakeLists.txt | 2 ++ src/platform/qt/src/widget/main_window.cpp | 7 ++--- src/platform/qt/src/widget/main_window.hpp | 3 +-- src/platform/qt/src/widget/sprite_viewer.cpp | 4 --- src/platform/qt/src/widget/sprite_viewer.hpp | 1 - .../qt/src/widget/sprite_viewer_window.cpp | 24 +++++++++++++++++ .../qt/src/widget/sprite_viewer_window.hpp | 26 +++++++++++++++++++ 7 files changed, 55 insertions(+), 12 deletions(-) create mode 100644 src/platform/qt/src/widget/sprite_viewer_window.cpp create mode 100644 src/platform/qt/src/widget/sprite_viewer_window.hpp diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 1a5a5501f..cc27f1455 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -15,6 +15,7 @@ set(SOURCES src/widget/palette_box.cpp src/widget/palette_viewer_window.cpp src/widget/sprite_viewer.cpp + src/widget/sprite_viewer_window.cpp src/config.cpp src/main.cpp ) @@ -29,6 +30,7 @@ set(HEADERS src/widget/palette_viewer_window.hpp src/widget/screen.hpp src/widget/sprite_viewer.hpp + src/widget/sprite_viewer_window.hpp src/config.hpp version.in.hpp ) diff --git a/src/platform/qt/src/widget/main_window.cpp b/src/platform/qt/src/widget/main_window.cpp index f7ec1a3bf..01bf66445 100644 --- a/src/platform/qt/src/widget/main_window.cpp +++ b/src/platform/qt/src/widget/main_window.cpp @@ -400,11 +400,8 @@ void MainWindow::CreateToolsMenu() { }); connect(tools_menu->addAction(tr("Sprite Viewer")), &QAction::triggered, [this]() { - if(!sprite_viewer_window) { - sprite_viewer_window = new SpriteViewer{core.get()}; - connect(screen.get(), &Screen::RequestDraw, sprite_viewer_window, &SpriteViewer::Update); - } - + const auto sprite_viewer_window = new SpriteViewerWindow{core.get(), this}; + connect(screen.get(), &Screen::RequestDraw, sprite_viewer_window, &SpriteViewerWindow::Update); sprite_viewer_window->show(); }); } diff --git a/src/platform/qt/src/widget/main_window.hpp b/src/platform/qt/src/widget/main_window.hpp index 3d9d2889a..eafe96245 100644 --- a/src/platform/qt/src/widget/main_window.hpp +++ b/src/platform/qt/src/widget/main_window.hpp @@ -27,7 +27,7 @@ #include "widget/input_window.hpp" #include "widget/palette_viewer_window.hpp" #include "widget/screen.hpp" -#include "widget/sprite_viewer.hpp" +#include "widget/sprite_viewer_window.hpp" #include "config.hpp" struct MainWindow : QMainWindow { @@ -153,7 +153,6 @@ private slots: PaletteViewerWindow* palette_viewer_window; BackgroundViewerWindow* background_viewer_window; - SpriteViewer* sprite_viewer_window; QString base_window_title; diff --git a/src/platform/qt/src/widget/sprite_viewer.cpp b/src/platform/qt/src/widget/sprite_viewer.cpp index 9e6dc5106..1c232d648 100644 --- a/src/platform/qt/src/widget/sprite_viewer.cpp +++ b/src/platform/qt/src/widget/sprite_viewer.cpp @@ -104,10 +104,6 @@ SpriteViewer::SpriteViewer(nba::CoreBase* core, QWidget* parent) : QWidget{paren atlas_canvas->installEventFilter(this); setLayout(layout); - - // @todo: move this out of here - setWindowTitle("Sprite Viewer"); - setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); } void SpriteViewer::Update() { diff --git a/src/platform/qt/src/widget/sprite_viewer.hpp b/src/platform/qt/src/widget/sprite_viewer.hpp index 645b8ce70..31acc5c26 100644 --- a/src/platform/qt/src/widget/sprite_viewer.hpp +++ b/src/platform/qt/src/widget/sprite_viewer.hpp @@ -22,7 +22,6 @@ struct SpriteViewer : QWidget { bool eventFilter(QObject* object, QEvent* event) override; -public slots: void Update(); private: diff --git a/src/platform/qt/src/widget/sprite_viewer_window.cpp b/src/platform/qt/src/widget/sprite_viewer_window.cpp new file mode 100644 index 000000000..0a88bf305 --- /dev/null +++ b/src/platform/qt/src/widget/sprite_viewer_window.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 fleroviux + * + * Licensed under GPLv3 or any later version. + * Refer to the included LICENSE file. + */ + +#include + +#include "sprite_viewer_window.hpp" + +SpriteViewerWindow::SpriteViewerWindow(nba::CoreBase* core, QWidget* parent) : QDialog(parent) { + sprite_viewer = new SpriteViewer{core, this}; + + setWindowTitle(tr("Sprite Viewer")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); +} + +void SpriteViewerWindow::Update() { + if(!isVisible()) { + return; + } + sprite_viewer->Update(); +} \ No newline at end of file diff --git a/src/platform/qt/src/widget/sprite_viewer_window.hpp b/src/platform/qt/src/widget/sprite_viewer_window.hpp new file mode 100644 index 000000000..9e10fff2a --- /dev/null +++ b/src/platform/qt/src/widget/sprite_viewer_window.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2023 fleroviux + * + * Licensed under GPLv3 or any later version. + * Refer to the included LICENSE file. + */ + +#pragma once + +#include +#include +#include + +#include "widget/sprite_viewer.hpp" + +struct SpriteViewerWindow : QDialog { + SpriteViewerWindow(nba::CoreBase* core, QWidget* parent = nullptr); + +public slots: + void Update(); + +private: + SpriteViewer* sprite_viewer; + + Q_OBJECT +}; From f7fa55bd1104cc0f32fadebb2329602739f66779 Mon Sep 17 00:00:00 2001 From: fleroviux Date: Wed, 27 Dec 2023 20:04:58 +0100 Subject: [PATCH 06/13] Qt: improve layout of the sprite viewer --- src/platform/qt/src/widget/sprite_viewer.cpp | 248 ++++++++---------- .../qt/src/widget/sprite_viewer_window.cpp | 6 +- 2 files changed, 108 insertions(+), 146 deletions(-) diff --git a/src/platform/qt/src/widget/sprite_viewer.cpp b/src/platform/qt/src/widget/sprite_viewer.cpp index 1c232d648..d25f5883d 100644 --- a/src/platform/qt/src/widget/sprite_viewer.cpp +++ b/src/platform/qt/src/widget/sprite_viewer.cpp @@ -44,37 +44,47 @@ static const auto CreateCheckBox = [](QCheckBox*& check_box) { SpriteViewer::SpriteViewer(nba::CoreBase* core, QWidget* parent) : QWidget{parent}, core{core} { + // TODO(fleroviux): do lots of cleanup. + pram = (u16*)core->GetPRAM(); vram = core->GetVRAM(); oam = core->GetOAM(); - QVBoxLayout* layout = new QVBoxLayout{}; + QHBoxLayout* hbox = new QHBoxLayout{}; + QVBoxLayout* vbox = new QVBoxLayout{}; - sprite_index_input = new QSpinBox{}; - sprite_index_input->setMinimum(0); - sprite_index_input->setMaximum(127); - layout->addWidget(sprite_index_input); + // OAM # and sprite magnification + { + sprite_index_input = new QSpinBox{}; + sprite_index_input->setMinimum(0); + sprite_index_input->setMaximum(127); - magnification_input = new QSpinBox{}; - magnification_input->setMinimum(1); - magnification_input->setMaximum(16); - layout->addWidget(magnification_input); + magnification_input = new QSpinBox{}; + magnification_input->setMinimum(1); + magnification_input->setMaximum(16); - canvas = new QWidget{}; - canvas->installEventFilter(this); - layout->addWidget(canvas); + const auto grid = new QGridLayout{}; + int row = 0; + grid->addWidget(new QLabel(tr("OAM #:")), row, 0); + grid->addWidget(sprite_index_input, row++, 1); + grid->addWidget(new QLabel(tr("Magnification:")), row, 0); + grid->addWidget(magnification_input, row++, 1); + vbox->addLayout(grid); + } + // Sprite attributes { QGroupBox* box = new QGroupBox{}; + box->setTitle("Object Attributes"); QGridLayout* grid = new QGridLayout{}; int row = 0; - + const auto PushRow = [&](const QString& label, QWidget* widget) { grid->addWidget(new QLabel{QStringLiteral("%1:").arg(label)}, row, 0); grid->addWidget(widget, row++, 1); }; - + PushRow("Enabled", CreateCheckBox(check_sprite_enabled)); PushRow("Position", CreateMonospaceLabel(label_sprite_position)); PushRow("Size", CreateMonospaceLabel(label_sprite_size)); @@ -90,30 +100,21 @@ SpriteViewer::SpriteViewer(nba::CoreBase* core, QWidget* parent) : QWidget{paren PushRow("Mosaic", CreateCheckBox(check_sprite_mosaic)); PushRow("Render cycles", CreateMonospaceLabel(label_sprite_render_cycles)); - // // Temporary workaround: - // grid->setColumnStretch(2, 100); - // grid->setRowStretch(row, 100); - box->setLayout(grid); - layout->addWidget(box); + vbox->addWidget(box); } - atlas_canvas = new QWidget{}; - atlas_canvas->setFixedSize(1024, 512); - layout->addWidget(atlas_canvas); - atlas_canvas->installEventFilter(this); + hbox->addLayout(vbox); + + canvas = new QWidget{}; + canvas->installEventFilter(this); + hbox->addWidget(canvas); - setLayout(layout); + setLayout(hbox); } void SpriteViewer::Update() { - const int index = sprite_index_input->value(); - - RenderSpriteAtlas(); - - RenderSprite(index, (u32*)image_rgb32.bits(), 64); - - const int offset = index << 3; + const int offset = sprite_index_input->value() << 3; const u16 attr0 = nba::read(oam, offset); const u16 attr1 = nba::read(oam, offset + 2); @@ -135,108 +136,13 @@ void SpriteViewer::Update() { const bool is_8bpp = attr0 & (1 << 13); const uint tile_number = attr2 & 0x3FFu; - static constexpr const char* k_mode_names[4] { - "Normal", "Semi-Transparent", "Window", "Prohibited" - }; - - const uint x = attr1 & 0x1FFu; - const uint y = attr0 & 0x0FFu; - const bool affine = attr0 & (1 << 8); - const uint palette = is_8bpp ? 0u : (attr2 >> 12); - const uint mode = (attr0 >> 10) & 3; - const bool mosaic = attr0 & (1 << 12); - - label_sprite_position->setText(QStringLiteral("%1, %2").arg(x).arg(y)); - label_sprite_size->setText(QStringLiteral("%1, %2").arg(width).arg(height)); - label_sprite_tile_number->setText(QStringLiteral("%1").arg(tile_number)); - label_sprite_palette->setText(QStringLiteral("%1").arg(palette)); - check_sprite_8bpp->setChecked(is_8bpp); - label_sprite_mode->setText(k_mode_names[mode]); - check_sprite_affine->setChecked(affine); - check_sprite_mosaic->setChecked(mosaic); - - check_sprite_vflip->setEnabled(!affine); - check_sprite_hflip->setEnabled(!affine); - check_sprite_double_size->setEnabled(affine); - - const int signed_x = x >= 240 ? ((int)x - 512) : (int)x; - - int render_cycles = 0; - - if(affine) { - const bool double_size = attr0 & (1 << 9); - const uint transform = (attr1 >> 9) & 31u; - - check_sprite_enabled->setChecked(true); - check_sprite_vflip->setChecked(false); - check_sprite_hflip->setChecked(false); - label_sprite_transform->setText(QStringLiteral("%1").arg(transform)); - check_sprite_double_size->setChecked(double_size); - label_sprite_render_cycles->setText("0 (0%)"); - - const int clipped_draw_width = std::max(0, (double_size ? 2 * width : width) + std::min(signed_x, 0)); - - render_cycles = 10 + 2 * clipped_draw_width; - } else { - const bool enabled = !(attr0 & (1 << 9)); - const bool hflip = attr1 & (1 << 12); - const bool vflip = attr1 & (1 << 13); - - check_sprite_enabled->setChecked(enabled); - check_sprite_vflip->setChecked(vflip); - check_sprite_hflip->setChecked(hflip); - label_sprite_transform->setText("n/a"); - check_sprite_double_size->setChecked(false); - label_sprite_render_cycles->setText("0 (0%)"); - - if(enabled) { - render_cycles = std::max(0, width + std::min(signed_x, 0)); - } - } - - const bool fast_hblank_oam_access = core->PeekHalfIO(0x04000000) & (1 << 5); - const int available_render_cycles = fast_hblank_oam_access ? 964 : 1232; - const float render_cycle_percentage = 100.0f * (float)render_cycles / (float)available_render_cycles; - - label_sprite_render_cycles->setText(QString::fromStdString(fmt::format("{} ({:.2f} %)", render_cycles, render_cycle_percentage))); - - sprite_width = width; - sprite_height = height; - - const int magnification = magnification_input->value(); - magnified_sprite_width = width * magnification; - magnified_sprite_height = height * magnification; - canvas->resize(magnified_sprite_width, magnified_sprite_height); - canvas->update(); -} - -void SpriteViewer::RenderSprite(int index, u32* buffer, int stride) { - static constexpr int k_sprite_size[4][4][2] = { - { { 8 , 8 }, { 16, 16 }, { 32, 32 }, { 64, 64 } }, // Square - { { 16, 8 }, { 32, 8 }, { 32, 16 }, { 64, 32 } }, // Horizontal - { { 8 , 16 }, { 8 , 32 }, { 16, 32 }, { 32, 64 } }, // Vertical - { { 8 , 8 }, { 8 , 8 }, { 8 , 8 }, { 8 , 8 } } // Prohibited - }; - - const int offset = index << 3; - - const u16 attr0 = nba::read(oam, offset); - const u16 attr1 = nba::read(oam, offset + 2); - const u16 attr2 = nba::read(oam, offset + 4); - - const int shape = attr0 >> 14; - const int size = attr1 >> 14; - const int width = k_sprite_size[shape][size][0]; - const int height = k_sprite_size[shape][size][1]; - - const bool is_8bpp = attr0 & (1 << 13); - const uint tile_number = attr2 & 0x3FFu; - const bool one_dimensional_mapping = core->PeekHalfIO(0x04000000) & (1 << 6); const int tiles_x = width >> 3; const int tiles_y = height >> 3; + u32* const buffer = (u32*)image_rgb32.bits(); + if(is_8bpp) { const u16* palette = &pram[256]; @@ -257,7 +163,7 @@ void SpriteViewer::RenderSprite(int index, u32* buffer, int stride) { for(int y = 0; y < 8; y++) { u64 tile_data = nba::read(vram, tile_address); - u32* dst = &buffer[((tile_y << 3) * stride) | (y * stride) | tile_x << 3]; + u32* dst = &buffer[tile_y << 9 | y << 6 | tile_x << 3]; for(int x = 0; x < 8; x++) { *dst++ = RGB555(palette[tile_data & 255u]); @@ -288,7 +194,7 @@ void SpriteViewer::RenderSprite(int index, u32* buffer, int stride) { for(int y = 0; y < 8; y++) { u32 tile_data = nba::read(vram, tile_address); - u32* dst = &buffer[((tile_y << 3) * stride) | (y * stride) | tile_x << 3]; + u32* dst = &buffer[tile_y << 9 | y << 6 | tile_x << 3]; for(int x = 0; x < 8; x++) { *dst++ = RGB555(palette[tile_data & 15u]); @@ -300,20 +206,78 @@ void SpriteViewer::RenderSprite(int index, u32* buffer, int stride) { } } } -} -void SpriteViewer::RenderSpriteAtlas() { - int sprite_index = 0; + static constexpr const char* k_mode_names[4] { + "Normal", "Semi-Transparent", "Window", "Prohibited" + }; - atlas_image_rgb32.fill(0xFF000000u); + const uint x = attr1 & 0x1FFu; + const uint y = attr0 & 0x0FFu; + const bool affine = attr0 & (1 << 8); + const uint palette = is_8bpp ? 0u : (attr2 >> 12); + const uint mode = (attr0 >> 10) & 3; + const bool mosaic = attr0 & (1 << 12); + + label_sprite_position->setText(QStringLiteral("%1, %2").arg(x).arg(y)); + label_sprite_size->setText(QStringLiteral("%1, %2").arg(width).arg(height)); + label_sprite_tile_number->setText(QStringLiteral("%1").arg(tile_number)); + label_sprite_palette->setText(QStringLiteral("%1").arg(palette)); + check_sprite_8bpp->setChecked(is_8bpp); + label_sprite_mode->setText(k_mode_names[mode]); + check_sprite_affine->setChecked(affine); + check_sprite_mosaic->setChecked(mosaic); + + check_sprite_vflip->setEnabled(!affine); + check_sprite_hflip->setEnabled(!affine); + check_sprite_double_size->setEnabled(affine); + + const int signed_x = x >= 240 ? ((int)x - 512) : (int)x; - for(int y = 0; y < 8; y++) { - for(int x = 0; x < 16; x++) { - RenderSprite(sprite_index++, &((u32*)atlas_image_rgb32.bits())[y * 65536 + x * 64], 1024); + int render_cycles = 0; + + if(affine) { + const bool double_size = attr0 & (1 << 9); + const uint transform = (attr1 >> 9) & 31u; + + check_sprite_enabled->setChecked(true); + check_sprite_vflip->setChecked(false); + check_sprite_hflip->setChecked(false); + label_sprite_transform->setText(QStringLiteral("%1").arg(transform)); + check_sprite_double_size->setChecked(double_size); + label_sprite_render_cycles->setText("0 (0%)"); + + const int clipped_draw_width = std::max(0, (double_size ? 2 * width : width) + std::min(signed_x, 0)); + + render_cycles = 10 + 2 * clipped_draw_width; + } else { + const bool enabled = !(attr0 & (1 << 9)); + const bool hflip = attr1 & (1 << 12); + const bool vflip = attr1 & (1 << 13); + + check_sprite_enabled->setChecked(enabled); + check_sprite_vflip->setChecked(vflip); + check_sprite_hflip->setChecked(hflip); + label_sprite_transform->setText("n/a"); + check_sprite_double_size->setChecked(false); + label_sprite_render_cycles->setText("0 (0%)"); + + if(enabled) { + render_cycles = std::max(0, width + std::min(signed_x, 0)); } } - atlas_canvas->update(); + const int available_render_cycles = (core->PeekHalfIO(0x04000000) & (1 << 5)) ? 964 : 1232; + + label_sprite_render_cycles->setText(QString::fromStdString(fmt::format("{} ({:.2f} %)", render_cycles, 100.0f * (float)render_cycles / available_render_cycles))); + + sprite_width = width; + sprite_height = height; + + const int magnification = magnification_input->value(); + magnified_sprite_width = width * magnification; + magnified_sprite_height = height * magnification; + canvas->resize(magnified_sprite_width, magnified_sprite_height); + canvas->update(); } bool SpriteViewer::eventFilter(QObject* object, QEvent* event) { @@ -323,13 +287,7 @@ bool SpriteViewer::eventFilter(QObject* object, QEvent* event) { QPainter painter{canvas}; painter.drawImage(dst_rect, image_rgb32, src_rect); - return true; - } - - if(object == atlas_canvas && event->type() == QEvent::Paint) { - QPainter painter{atlas_canvas}; - painter.drawImage(0, 0, atlas_image_rgb32); - return true; + //return true; } return false; diff --git a/src/platform/qt/src/widget/sprite_viewer_window.cpp b/src/platform/qt/src/widget/sprite_viewer_window.cpp index 0a88bf305..46e1f89b6 100644 --- a/src/platform/qt/src/widget/sprite_viewer_window.cpp +++ b/src/platform/qt/src/widget/sprite_viewer_window.cpp @@ -10,7 +10,11 @@ #include "sprite_viewer_window.hpp" SpriteViewerWindow::SpriteViewerWindow(nba::CoreBase* core, QWidget* parent) : QDialog(parent) { - sprite_viewer = new SpriteViewer{core, this}; + const auto vbox = new QVBoxLayout{}; + + sprite_viewer = new SpriteViewer{core, nullptr}; + vbox->addWidget(sprite_viewer); + setLayout(vbox); setWindowTitle(tr("Sprite Viewer")); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); From 361347b1b95abcdb0e73bfca692f655b840efffd Mon Sep 17 00:00:00 2001 From: fleroviux Date: Thu, 28 Dec 2023 11:04:28 +0100 Subject: [PATCH 07/13] Qt: improve sprite viewer layout a bit --- src/platform/qt/src/widget/sprite_viewer.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/platform/qt/src/widget/sprite_viewer.cpp b/src/platform/qt/src/widget/sprite_viewer.cpp index d25f5883d..49188ba60 100644 --- a/src/platform/qt/src/widget/sprite_viewer.cpp +++ b/src/platform/qt/src/widget/sprite_viewer.cpp @@ -104,13 +104,17 @@ SpriteViewer::SpriteViewer(nba::CoreBase* core, QWidget* parent) : QWidget{paren vbox->addWidget(box); } + vbox->addStretch(); + hbox->addLayout(vbox); canvas = new QWidget{}; canvas->installEventFilter(this); hbox->addWidget(canvas); - setLayout(hbox); + hbox->setStretch(1, 1); + + setLayout(hbox); } void SpriteViewer::Update() { From 6d07124b834cf52df14ae06551a1141744503770 Mon Sep 17 00:00:00 2001 From: fleroviux Date: Thu, 28 Dec 2023 12:57:15 +0100 Subject: [PATCH 08/13] Qt: set minimum width for sprite attribute group box --- src/platform/qt/src/widget/sprite_viewer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/platform/qt/src/widget/sprite_viewer.cpp b/src/platform/qt/src/widget/sprite_viewer.cpp index 49188ba60..607ad3da5 100644 --- a/src/platform/qt/src/widget/sprite_viewer.cpp +++ b/src/platform/qt/src/widget/sprite_viewer.cpp @@ -77,6 +77,7 @@ SpriteViewer::SpriteViewer(nba::CoreBase* core, QWidget* parent) : QWidget{paren QGroupBox* box = new QGroupBox{}; box->setTitle("Object Attributes"); QGridLayout* grid = new QGridLayout{}; + box->setMinimumWidth(230); int row = 0; From 14dff18c8a9b60235e9da2d1dc6d38417b229896 Mon Sep 17 00:00:00 2001 From: fleroviux Date: Thu, 28 Dec 2023 13:09:33 +0100 Subject: [PATCH 09/13] Qt: cosmetic debugger UI adjustments --- src/platform/qt/src/widget/background_viewer.cpp | 2 ++ src/platform/qt/src/widget/sprite_viewer.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/platform/qt/src/widget/background_viewer.cpp b/src/platform/qt/src/widget/background_viewer.cpp index a5dabeb68..44e07547a 100644 --- a/src/platform/qt/src/widget/background_viewer.cpp +++ b/src/platform/qt/src/widget/background_viewer.cpp @@ -66,6 +66,7 @@ BackgroundViewer::BackgroundViewer(nba::CoreBase* core, QWidget* parent) : QWidg const auto group_box = new QGroupBox{}; group_box->setLayout(grid); group_box->setTitle(tr("Background")); + group_box->setMinimumWidth(220); info_vbox->addWidget(group_box); } @@ -100,6 +101,7 @@ BackgroundViewer::BackgroundViewer(nba::CoreBase* core, QWidget* parent) : QWidg const auto group_box = new QGroupBox{}; group_box->setLayout(grid); group_box->setTitle(tr("Tile")); + group_box->setMinimumWidth(220); info_vbox->addWidget(group_box); diff --git a/src/platform/qt/src/widget/sprite_viewer.cpp b/src/platform/qt/src/widget/sprite_viewer.cpp index 607ad3da5..768efef5f 100644 --- a/src/platform/qt/src/widget/sprite_viewer.cpp +++ b/src/platform/qt/src/widget/sprite_viewer.cpp @@ -77,7 +77,7 @@ SpriteViewer::SpriteViewer(nba::CoreBase* core, QWidget* parent) : QWidget{paren QGroupBox* box = new QGroupBox{}; box->setTitle("Object Attributes"); QGridLayout* grid = new QGridLayout{}; - box->setMinimumWidth(230); + box->setMinimumWidth(220); int row = 0; From c1fc159cf6e1c9ad05929c6556147fa58256ad64 Mon Sep 17 00:00:00 2001 From: fleroviux Date: Thu, 28 Dec 2023 16:51:54 +0100 Subject: [PATCH 10/13] Qt: improve sprite viewer layout --- src/platform/qt/src/widget/sprite_viewer.cpp | 127 ++++++++++--------- src/platform/qt/src/widget/sprite_viewer.hpp | 4 - 2 files changed, 69 insertions(+), 62 deletions(-) diff --git a/src/platform/qt/src/widget/sprite_viewer.cpp b/src/platform/qt/src/widget/sprite_viewer.cpp index 768efef5f..a3347db9c 100644 --- a/src/platform/qt/src/widget/sprite_viewer.cpp +++ b/src/platform/qt/src/widget/sprite_viewer.cpp @@ -46,74 +46,85 @@ static const auto CreateCheckBox = [](QCheckBox*& check_box) { SpriteViewer::SpriteViewer(nba::CoreBase* core, QWidget* parent) : QWidget{parent}, core{core} { // TODO(fleroviux): do lots of cleanup. - pram = (u16*)core->GetPRAM(); + pram = (u16 *) core->GetPRAM(); vram = core->GetVRAM(); oam = core->GetOAM(); QHBoxLayout* hbox = new QHBoxLayout{}; - QVBoxLayout* vbox = new QVBoxLayout{}; - // OAM # and sprite magnification { - sprite_index_input = new QSpinBox{}; - sprite_index_input->setMinimum(0); - sprite_index_input->setMaximum(127); - - magnification_input = new QSpinBox{}; - magnification_input->setMinimum(1); - magnification_input->setMaximum(16); - - const auto grid = new QGridLayout{}; - int row = 0; - grid->addWidget(new QLabel(tr("OAM #:")), row, 0); - grid->addWidget(sprite_index_input, row++, 1); - grid->addWidget(new QLabel(tr("Magnification:")), row, 0); - grid->addWidget(magnification_input, row++, 1); - vbox->addLayout(grid); - } + QVBoxLayout* vbox = new QVBoxLayout{}; + + // OAM # and sprite magnification + { + sprite_index_input = new QSpinBox{}; + sprite_index_input->setMinimum(0); + sprite_index_input->setMaximum(127); + + magnification_input = new QSpinBox{}; + magnification_input->setMinimum(1); + magnification_input->setMaximum(16); + + const auto grid = new QGridLayout{}; + int row = 0; + grid->addWidget(new QLabel(tr("OAM #:")), row, 0); + grid->addWidget(sprite_index_input, row++, 1); + grid->addWidget(new QLabel(tr("Magnification:")), row, 0); + grid->addWidget(magnification_input, row++, 1); + vbox->addLayout(grid); + } - // Sprite attributes - { - QGroupBox* box = new QGroupBox{}; - box->setTitle("Object Attributes"); - QGridLayout* grid = new QGridLayout{}; - box->setMinimumWidth(220); - - int row = 0; - - const auto PushRow = [&](const QString& label, QWidget* widget) { - grid->addWidget(new QLabel{QStringLiteral("%1:").arg(label)}, row, 0); - grid->addWidget(widget, row++, 1); - }; - - PushRow("Enabled", CreateCheckBox(check_sprite_enabled)); - PushRow("Position", CreateMonospaceLabel(label_sprite_position)); - PushRow("Size", CreateMonospaceLabel(label_sprite_size)); - PushRow("Tile number", CreateMonospaceLabel(label_sprite_tile_number)); - PushRow("Palette", CreateMonospaceLabel(label_sprite_palette)); - PushRow("8BPP", CreateCheckBox(check_sprite_8bpp)); - PushRow("Flip V", CreateCheckBox(check_sprite_vflip)); - PushRow("Flip H", CreateCheckBox(check_sprite_hflip)); - PushRow("Mode", CreateMonospaceLabel(label_sprite_mode)); - PushRow("Affine", CreateCheckBox(check_sprite_affine)); - PushRow("Transform #", CreateMonospaceLabel(label_sprite_transform)); - PushRow("Double-size", CreateCheckBox(check_sprite_double_size)); - PushRow("Mosaic", CreateCheckBox(check_sprite_mosaic)); - PushRow("Render cycles", CreateMonospaceLabel(label_sprite_render_cycles)); - - box->setLayout(grid); - vbox->addWidget(box); + // Sprite attributes + { + QGroupBox *box = new QGroupBox{}; + box->setTitle("Object Attributes"); + QGridLayout *grid = new QGridLayout{}; + box->setMinimumWidth(235); + + int row = 0; + + const auto PushRow = [&](const QString &label, QWidget *widget) { + grid->addWidget(new QLabel{QStringLiteral("%1:").arg(label)}, row, 0); + grid->addWidget(widget, row++, 1); + }; + + PushRow("Enabled", CreateCheckBox(check_sprite_enabled)); + PushRow("Position", CreateMonospaceLabel(label_sprite_position)); + PushRow("Size", CreateMonospaceLabel(label_sprite_size)); + PushRow("Tile number", CreateMonospaceLabel(label_sprite_tile_number)); + PushRow("Palette", CreateMonospaceLabel(label_sprite_palette)); + PushRow("8BPP", CreateCheckBox(check_sprite_8bpp)); + PushRow("Flip V", CreateCheckBox(check_sprite_vflip)); + PushRow("Flip H", CreateCheckBox(check_sprite_hflip)); + PushRow("Mode", CreateMonospaceLabel(label_sprite_mode)); + PushRow("Affine", CreateCheckBox(check_sprite_affine)); + PushRow("Transform #", CreateMonospaceLabel(label_sprite_transform)); + PushRow("Double-size", CreateCheckBox(check_sprite_double_size)); + PushRow("Mosaic", CreateCheckBox(check_sprite_mosaic)); + PushRow("Render cycles", CreateMonospaceLabel(label_sprite_render_cycles)); + + box->setLayout(grid); + vbox->addWidget(box); + } + + vbox->addStretch(); + hbox->addLayout(vbox); } - vbox->addStretch(); + { + QVBoxLayout* vbox = new QVBoxLayout{}; - hbox->addLayout(vbox); + canvas = new QWidget{}; + canvas->setFixedSize(64, 64); + canvas->setFixedSize(64, 64); + canvas->installEventFilter(this); + vbox->addWidget(canvas); + vbox->addStretch(1); - canvas = new QWidget{}; - canvas->installEventFilter(this); - hbox->addWidget(canvas); + hbox->addLayout(vbox); + } - hbox->setStretch(1, 1); + hbox->addStretch(1); setLayout(hbox); } @@ -281,7 +292,7 @@ void SpriteViewer::Update() { const int magnification = magnification_input->value(); magnified_sprite_width = width * magnification; magnified_sprite_height = height * magnification; - canvas->resize(magnified_sprite_width, magnified_sprite_height); + canvas->setFixedSize(magnified_sprite_width, magnified_sprite_height); canvas->update(); } @@ -292,7 +303,7 @@ bool SpriteViewer::eventFilter(QObject* object, QEvent* event) { QPainter painter{canvas}; painter.drawImage(dst_rect, image_rgb32, src_rect); - //return true; + return true; } return false; diff --git a/src/platform/qt/src/widget/sprite_viewer.hpp b/src/platform/qt/src/widget/sprite_viewer.hpp index 31acc5c26..32ad0e234 100644 --- a/src/platform/qt/src/widget/sprite_viewer.hpp +++ b/src/platform/qt/src/widget/sprite_viewer.hpp @@ -26,7 +26,6 @@ struct SpriteViewer : QWidget { private: void RenderSprite(int index, u32* buffer, int stride); - void RenderSpriteAtlas(); nba::CoreBase* core; u16* pram; @@ -38,9 +37,6 @@ struct SpriteViewer : QWidget { QSpinBox* magnification_input; QWidget* canvas; - QImage atlas_image_rgb32{1024, 512, QImage::Format_RGB32}; - QWidget* atlas_canvas; - QCheckBox* check_sprite_enabled; QLabel* label_sprite_position; QLabel* label_sprite_size; From 57b86a69b93d12d40db93fdb30e95a6e512d6df1 Mon Sep 17 00:00:00 2001 From: fleroviux Date: Fri, 29 Dec 2023 11:15:58 +0100 Subject: [PATCH 11/13] Cleanup --- src/platform/qt/src/widget/sprite_viewer.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/platform/qt/src/widget/sprite_viewer.hpp b/src/platform/qt/src/widget/sprite_viewer.hpp index 32ad0e234..cbeb9de12 100644 --- a/src/platform/qt/src/widget/sprite_viewer.hpp +++ b/src/platform/qt/src/widget/sprite_viewer.hpp @@ -25,8 +25,6 @@ struct SpriteViewer : QWidget { void Update(); private: - void RenderSprite(int index, u32* buffer, int stride); - nba::CoreBase* core; u16* pram; u8* vram; From 445fe2489d0c4c3f4f87524932ed7a1b6a5316b2 Mon Sep 17 00:00:00 2001 From: fleroviux Date: Fri, 29 Dec 2023 11:35:22 +0100 Subject: [PATCH 12/13] Move debug UI classes into a subfolder --- src/platform/qt/CMakeLists.txt | 24 +++++++++---------- src/platform/qt/src/widget/debugger/.gitkeep | 0 .../{ => debugger/ppu}/background_viewer.cpp | 0 .../{ => debugger/ppu}/background_viewer.hpp | 2 +- .../ppu}/background_viewer_window.cpp | 0 .../ppu}/background_viewer_window.hpp | 2 +- .../widget/{ => debugger/ppu}/palette_box.cpp | 0 .../widget/{ => debugger/ppu}/palette_box.hpp | 0 .../ppu}/palette_viewer_window.cpp | 0 .../ppu}/palette_viewer_window.hpp | 2 +- .../{ => debugger/ppu}/sprite_viewer.cpp | 0 .../{ => debugger/ppu}/sprite_viewer.hpp | 0 .../ppu}/sprite_viewer_window.cpp | 0 .../ppu}/sprite_viewer_window.hpp | 2 +- src/platform/qt/src/widget/main_window.hpp | 7 +++--- 15 files changed, 19 insertions(+), 20 deletions(-) create mode 100644 src/platform/qt/src/widget/debugger/.gitkeep rename src/platform/qt/src/widget/{ => debugger/ppu}/background_viewer.cpp (100%) rename src/platform/qt/src/widget/{ => debugger/ppu}/background_viewer.hpp (98%) rename src/platform/qt/src/widget/{ => debugger/ppu}/background_viewer_window.cpp (100%) rename src/platform/qt/src/widget/{ => debugger/ppu}/background_viewer_window.hpp (91%) rename src/platform/qt/src/widget/{ => debugger/ppu}/palette_box.cpp (100%) rename src/platform/qt/src/widget/{ => debugger/ppu}/palette_box.hpp (100%) rename src/platform/qt/src/widget/{ => debugger/ppu}/palette_viewer_window.cpp (100%) rename src/platform/qt/src/widget/{ => debugger/ppu}/palette_viewer_window.hpp (94%) rename src/platform/qt/src/widget/{ => debugger/ppu}/sprite_viewer.cpp (100%) rename src/platform/qt/src/widget/{ => debugger/ppu}/sprite_viewer.hpp (100%) rename src/platform/qt/src/widget/{ => debugger/ppu}/sprite_viewer_window.cpp (100%) rename src/platform/qt/src/widget/{ => debugger/ppu}/sprite_viewer_window.hpp (91%) diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index d47b5229e..1d8f80d12 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -2,31 +2,31 @@ include(version.cmake) set(SOURCES - src/widget/background_viewer_window.cpp - src/widget/background_viewer.cpp + src/widget/debugger/ppu/background_viewer_window.cpp + src/widget/debugger/ppu/background_viewer.cpp src/widget/controller_manager.cpp src/widget/input_window.cpp src/widget/main_window.cpp src/widget/screen.cpp - src/widget/sprite_viewer.cpp - src/widget/sprite_viewer_window.cpp - src/widget/palette_box.cpp - src/widget/palette_viewer_window.cpp + src/widget/debugger/ppu/sprite_viewer.cpp + src/widget/debugger/ppu/sprite_viewer_window.cpp + src/widget/debugger/ppu/palette_box.cpp + src/widget/debugger/ppu/palette_viewer_window.cpp src/config.cpp src/main.cpp ) set(HEADERS - src/widget/background_viewer_window.hpp - src/widget/background_viewer.hpp + src/widget/debugger/ppu/background_viewer_window.hpp + src/widget/debugger/ppu/background_viewer.hpp src/widget/controller_manager.hpp src/widget/input_window.hpp src/widget/main_window.hpp - src/widget/palette_box.hpp - src/widget/palette_viewer_window.hpp + src/widget/debugger/ppu/palette_box.hpp + src/widget/debugger/ppu/palette_viewer_window.hpp src/widget/screen.hpp - src/widget/sprite_viewer.hpp - src/widget/sprite_viewer_window.hpp + src/widget/debugger/ppu/sprite_viewer.hpp + src/widget/debugger/ppu/sprite_viewer_window.hpp src/config.hpp version.in.hpp ) diff --git a/src/platform/qt/src/widget/debugger/.gitkeep b/src/platform/qt/src/widget/debugger/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/platform/qt/src/widget/background_viewer.cpp b/src/platform/qt/src/widget/debugger/ppu/background_viewer.cpp similarity index 100% rename from src/platform/qt/src/widget/background_viewer.cpp rename to src/platform/qt/src/widget/debugger/ppu/background_viewer.cpp diff --git a/src/platform/qt/src/widget/background_viewer.hpp b/src/platform/qt/src/widget/debugger/ppu/background_viewer.hpp similarity index 98% rename from src/platform/qt/src/widget/background_viewer.hpp rename to src/platform/qt/src/widget/debugger/ppu/background_viewer.hpp index 4dd3464c9..35cab1112 100644 --- a/src/platform/qt/src/widget/background_viewer.hpp +++ b/src/platform/qt/src/widget/debugger/ppu/background_viewer.hpp @@ -13,7 +13,7 @@ #include #include -#include "widget/palette_box.hpp" +#include "palette_box.hpp" struct BackgroundViewer : QWidget { BackgroundViewer(nba::CoreBase* core, QWidget* parent = nullptr); diff --git a/src/platform/qt/src/widget/background_viewer_window.cpp b/src/platform/qt/src/widget/debugger/ppu/background_viewer_window.cpp similarity index 100% rename from src/platform/qt/src/widget/background_viewer_window.cpp rename to src/platform/qt/src/widget/debugger/ppu/background_viewer_window.cpp diff --git a/src/platform/qt/src/widget/background_viewer_window.hpp b/src/platform/qt/src/widget/debugger/ppu/background_viewer_window.hpp similarity index 91% rename from src/platform/qt/src/widget/background_viewer_window.hpp rename to src/platform/qt/src/widget/debugger/ppu/background_viewer_window.hpp index 79ee543b0..8245fa178 100644 --- a/src/platform/qt/src/widget/background_viewer_window.hpp +++ b/src/platform/qt/src/widget/debugger/ppu/background_viewer_window.hpp @@ -11,7 +11,7 @@ #include #include -#include "widget/background_viewer.hpp" +#include "background_viewer.hpp" struct BackgroundViewerWindow : QDialog { BackgroundViewerWindow(nba::CoreBase* core, QWidget* parent = nullptr); diff --git a/src/platform/qt/src/widget/palette_box.cpp b/src/platform/qt/src/widget/debugger/ppu/palette_box.cpp similarity index 100% rename from src/platform/qt/src/widget/palette_box.cpp rename to src/platform/qt/src/widget/debugger/ppu/palette_box.cpp diff --git a/src/platform/qt/src/widget/palette_box.hpp b/src/platform/qt/src/widget/debugger/ppu/palette_box.hpp similarity index 100% rename from src/platform/qt/src/widget/palette_box.hpp rename to src/platform/qt/src/widget/debugger/ppu/palette_box.hpp diff --git a/src/platform/qt/src/widget/palette_viewer_window.cpp b/src/platform/qt/src/widget/debugger/ppu/palette_viewer_window.cpp similarity index 100% rename from src/platform/qt/src/widget/palette_viewer_window.cpp rename to src/platform/qt/src/widget/debugger/ppu/palette_viewer_window.cpp diff --git a/src/platform/qt/src/widget/palette_viewer_window.hpp b/src/platform/qt/src/widget/debugger/ppu/palette_viewer_window.hpp similarity index 94% rename from src/platform/qt/src/widget/palette_viewer_window.hpp rename to src/platform/qt/src/widget/debugger/ppu/palette_viewer_window.hpp index 08f83c17f..8fe619bd0 100644 --- a/src/platform/qt/src/widget/palette_viewer_window.hpp +++ b/src/platform/qt/src/widget/debugger/ppu/palette_viewer_window.hpp @@ -11,7 +11,7 @@ #include #include -#include "widget/palette_box.hpp" +#include "palette_box.hpp" struct PaletteViewerWindow : QDialog { PaletteViewerWindow(nba::CoreBase* core, QWidget* parent = nullptr); diff --git a/src/platform/qt/src/widget/sprite_viewer.cpp b/src/platform/qt/src/widget/debugger/ppu/sprite_viewer.cpp similarity index 100% rename from src/platform/qt/src/widget/sprite_viewer.cpp rename to src/platform/qt/src/widget/debugger/ppu/sprite_viewer.cpp diff --git a/src/platform/qt/src/widget/sprite_viewer.hpp b/src/platform/qt/src/widget/debugger/ppu/sprite_viewer.hpp similarity index 100% rename from src/platform/qt/src/widget/sprite_viewer.hpp rename to src/platform/qt/src/widget/debugger/ppu/sprite_viewer.hpp diff --git a/src/platform/qt/src/widget/sprite_viewer_window.cpp b/src/platform/qt/src/widget/debugger/ppu/sprite_viewer_window.cpp similarity index 100% rename from src/platform/qt/src/widget/sprite_viewer_window.cpp rename to src/platform/qt/src/widget/debugger/ppu/sprite_viewer_window.cpp diff --git a/src/platform/qt/src/widget/sprite_viewer_window.hpp b/src/platform/qt/src/widget/debugger/ppu/sprite_viewer_window.hpp similarity index 91% rename from src/platform/qt/src/widget/sprite_viewer_window.hpp rename to src/platform/qt/src/widget/debugger/ppu/sprite_viewer_window.hpp index 9e10fff2a..57a002106 100644 --- a/src/platform/qt/src/widget/sprite_viewer_window.hpp +++ b/src/platform/qt/src/widget/debugger/ppu/sprite_viewer_window.hpp @@ -11,7 +11,7 @@ #include #include -#include "widget/sprite_viewer.hpp" +#include "sprite_viewer.hpp" struct SpriteViewerWindow : QDialog { SpriteViewerWindow(nba::CoreBase* core, QWidget* parent = nullptr); diff --git a/src/platform/qt/src/widget/main_window.hpp b/src/platform/qt/src/widget/main_window.hpp index eafe96245..1a3c66739 100644 --- a/src/platform/qt/src/widget/main_window.hpp +++ b/src/platform/qt/src/widget/main_window.hpp @@ -22,12 +22,12 @@ #include #include -#include "widget/background_viewer_window.hpp" +#include "widget/debugger/ppu/background_viewer_window.hpp" #include "widget/controller_manager.hpp" #include "widget/input_window.hpp" -#include "widget/palette_viewer_window.hpp" +#include "widget/debugger/ppu/palette_viewer_window.hpp" #include "widget/screen.hpp" -#include "widget/sprite_viewer_window.hpp" +#include "widget/debugger/ppu/sprite_viewer_window.hpp" #include "config.hpp" struct MainWindow : QMainWindow { @@ -70,7 +70,6 @@ private slots: void SelectBIOS(); void SelectSaveFolder(); void RemoveSaveFolder(); - void ClearSaveFolder(); void PromptUserForReset(); auto CreateBooleanOption( From d8ecb24b53960403bf56260f907e73a3518743b5 Mon Sep 17 00:00:00 2001 From: fleroviux Date: Fri, 29 Dec 2023 12:05:58 +0100 Subject: [PATCH 13/13] Deduplicate RGB565 decoding in the debugger UI --- src/platform/qt/CMakeLists.txt | 17 +++++++++-------- src/platform/qt/src/widget/debugger/.gitkeep | 0 .../widget/debugger/ppu/background_viewer.cpp | 10 ++-------- .../qt/src/widget/debugger/ppu/palette_box.cpp | 9 ++------- .../src/widget/debugger/ppu/sprite_viewer.cpp | 15 +++------------ src/platform/qt/src/widget/debugger/utility.hpp | 15 +++++++++++++++ 6 files changed, 31 insertions(+), 35 deletions(-) delete mode 100644 src/platform/qt/src/widget/debugger/.gitkeep create mode 100644 src/platform/qt/src/widget/debugger/utility.hpp diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 1d8f80d12..4a1732616 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -4,14 +4,14 @@ include(version.cmake) set(SOURCES src/widget/debugger/ppu/background_viewer_window.cpp src/widget/debugger/ppu/background_viewer.cpp - src/widget/controller_manager.cpp - src/widget/input_window.cpp - src/widget/main_window.cpp - src/widget/screen.cpp src/widget/debugger/ppu/sprite_viewer.cpp src/widget/debugger/ppu/sprite_viewer_window.cpp src/widget/debugger/ppu/palette_box.cpp src/widget/debugger/ppu/palette_viewer_window.cpp + src/widget/controller_manager.cpp + src/widget/input_window.cpp + src/widget/main_window.cpp + src/widget/screen.cpp src/config.cpp src/main.cpp ) @@ -19,14 +19,15 @@ set(SOURCES set(HEADERS src/widget/debugger/ppu/background_viewer_window.hpp src/widget/debugger/ppu/background_viewer.hpp - src/widget/controller_manager.hpp - src/widget/input_window.hpp - src/widget/main_window.hpp src/widget/debugger/ppu/palette_box.hpp src/widget/debugger/ppu/palette_viewer_window.hpp - src/widget/screen.hpp src/widget/debugger/ppu/sprite_viewer.hpp src/widget/debugger/ppu/sprite_viewer_window.hpp + src/widget/debugger/utility.hpp + src/widget/controller_manager.hpp + src/widget/input_window.hpp + src/widget/main_window.hpp + src/widget/screen.hpp src/config.hpp version.in.hpp ) diff --git a/src/platform/qt/src/widget/debugger/.gitkeep b/src/platform/qt/src/widget/debugger/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/platform/qt/src/widget/debugger/ppu/background_viewer.cpp b/src/platform/qt/src/widget/debugger/ppu/background_viewer.cpp index 44e07547a..0dc514713 100644 --- a/src/platform/qt/src/widget/debugger/ppu/background_viewer.cpp +++ b/src/platform/qt/src/widget/debugger/ppu/background_viewer.cpp @@ -18,6 +18,7 @@ #include #include +#include "widget/debugger/utility.hpp" #include "background_viewer.hpp" BackgroundViewer::BackgroundViewer(nba::CoreBase* core, QWidget* parent) : QWidget(parent), core(core) { @@ -433,14 +434,7 @@ void BackgroundViewer::PresentBackground() { for(int y = 0; y < height; y++) { for(int x = 0; x < width; x++) { - const u16 color_rgb565 = image_rgb565[i]; - - // @todo: de-duplicate the RGB565 to RGB32 conversion. - const int r = (color_rgb565 >> 0) & 31; - const int g = ((color_rgb565 >> 4) & 62) | (color_rgb565 >> 15); - const int b = (color_rgb565 >> 10) & 31; - - destination[i++] = 0xFF000000 | (r << 3 | r >> 2) << 16 | (g << 2 | g >> 4) << 8 | (b << 3 | b >> 2); + destination[i++] = Rgb565ToArgb8888(image_rgb565[i]); } i += skip; diff --git a/src/platform/qt/src/widget/debugger/ppu/palette_box.cpp b/src/platform/qt/src/widget/debugger/ppu/palette_box.cpp index e6a7d82ab..247c09ec0 100644 --- a/src/platform/qt/src/widget/debugger/ppu/palette_box.cpp +++ b/src/platform/qt/src/widget/debugger/ppu/palette_box.cpp @@ -9,6 +9,7 @@ #include #include +#include "widget/debugger/utility.hpp" #include "palette_box.hpp" PaletteBox::PaletteBox(int rows, int columns, QWidget* parent) : QWidget(parent), rows(rows), columns(columns) { @@ -27,13 +28,7 @@ void PaletteBox::Draw(u16* palette_rgb565, int stride) { for(int y = 0; y < rows; y++) { for(int x = 0; x < columns; x++) { - const u16 color_rgb565 = palette_rgb565[y * stride + x]; - - const int r = (color_rgb565 >> 0) & 31; - const int g = ((color_rgb565 >> 4) & 62) | (color_rgb565 >> 15); - const int b = (color_rgb565 >> 10) & 31; - - palette_argb8888[i++] = 0xFF000000 | (r << 3 | r >> 2) << 16 | (g << 2 | g >> 4) << 8 | (b << 3 | b >> 2); + palette_argb8888[i++] = Rgb565ToArgb8888(palette_rgb565[y * stride + x]); } } diff --git a/src/platform/qt/src/widget/debugger/ppu/sprite_viewer.cpp b/src/platform/qt/src/widget/debugger/ppu/sprite_viewer.cpp index a3347db9c..7efec1ad6 100644 --- a/src/platform/qt/src/widget/debugger/ppu/sprite_viewer.cpp +++ b/src/platform/qt/src/widget/debugger/ppu/sprite_viewer.cpp @@ -13,20 +13,11 @@ #include #include +#include "widget/debugger/utility.hpp" #include "sprite_viewer.hpp" // -------------------------------------------------------------------- -// @todo: deduplicate these helpers across all viewers - -static u32 RGB555(u16 rgb555) { - const uint r = (rgb555 >> 0) & 31U; - const uint g = (rgb555 >> 5) & 31U; - const uint b = (rgb555 >> 10) & 31U; - - return 0xFF000000 | (r << 3 | r >> 2) << 16 | (g << 3 | g >> 2) << 8 | (b << 3 | b >> 2); -} - static const auto CreateMonospaceLabel = [](QLabel*& label) { label = new QLabel{"-"}; label->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); @@ -182,7 +173,7 @@ void SpriteViewer::Update() { u32* dst = &buffer[tile_y << 9 | y << 6 | tile_x << 3]; for(int x = 0; x < 8; x++) { - *dst++ = RGB555(palette[tile_data & 255u]); + *dst++ = Rgb565ToArgb8888(palette[tile_data & 255u]); tile_data >>= 8; } @@ -213,7 +204,7 @@ void SpriteViewer::Update() { u32* dst = &buffer[tile_y << 9 | y << 6 | tile_x << 3]; for(int x = 0; x < 8; x++) { - *dst++ = RGB555(palette[tile_data & 15u]); + *dst++ = Rgb565ToArgb8888(palette[tile_data & 15u]); tile_data >>= 4; } diff --git a/src/platform/qt/src/widget/debugger/utility.hpp b/src/platform/qt/src/widget/debugger/utility.hpp new file mode 100644 index 000000000..f97ad667b --- /dev/null +++ b/src/platform/qt/src/widget/debugger/utility.hpp @@ -0,0 +1,15 @@ + +#pragma once + +#include + +constexpr u32 Rgb565ToArgb8888(u16 color_rgb565) { + const uint r = (color_rgb565 >> 0) & 31; + const uint g = ((color_rgb565 >> 4) & 62) | (color_rgb565 >> 15); + const uint b = (color_rgb565 >> 10) & 31; + + return 0xFF000000u | + (r << 3 | r >> 2) << 16 | + (g << 2 | g >> 4) << 8 | + (b << 3 | b >> 2); +} \ No newline at end of file