From d26b39c1414671ade3aec5f4830036da8afef526 Mon Sep 17 00:00:00 2001 From: fleroviux Date: Wed, 14 Feb 2024 18:58:47 +0100 Subject: [PATCH] Tile Viewer WIP --- src/platform/qt/CMakeLists.txt | 4 + .../src/widget/debugger/ppu/tile_viewer.cpp | 145 ++++++++++++++++++ .../src/widget/debugger/ppu/tile_viewer.hpp | 36 +++++ .../debugger/ppu/tile_viewer_window.cpp | 28 ++++ .../debugger/ppu/tile_viewer_window.hpp | 26 ++++ src/platform/qt/src/widget/main_window.cpp | 9 ++ src/platform/qt/src/widget/main_window.hpp | 6 +- 7 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 src/platform/qt/src/widget/debugger/ppu/tile_viewer.cpp create mode 100644 src/platform/qt/src/widget/debugger/ppu/tile_viewer.hpp create mode 100644 src/platform/qt/src/widget/debugger/ppu/tile_viewer_window.cpp create mode 100644 src/platform/qt/src/widget/debugger/ppu/tile_viewer_window.hpp diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 4a173261..3301bac6 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -8,6 +8,8 @@ set(SOURCES 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/debugger/ppu/tile_viewer.cpp + src/widget/debugger/ppu/tile_viewer_window.cpp src/widget/controller_manager.cpp src/widget/input_window.cpp src/widget/main_window.cpp @@ -23,6 +25,8 @@ set(HEADERS src/widget/debugger/ppu/palette_viewer_window.hpp src/widget/debugger/ppu/sprite_viewer.hpp src/widget/debugger/ppu/sprite_viewer_window.hpp + src/widget/debugger/ppu/tile_viewer.hpp + src/widget/debugger/ppu/tile_viewer_window.hpp src/widget/debugger/utility.hpp src/widget/controller_manager.hpp src/widget/input_window.hpp diff --git a/src/platform/qt/src/widget/debugger/ppu/tile_viewer.cpp b/src/platform/qt/src/widget/debugger/ppu/tile_viewer.cpp new file mode 100644 index 00000000..2218bc1d --- /dev/null +++ b/src/platform/qt/src/widget/debugger/ppu/tile_viewer.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2024 fleroviux + * + * Licensed under GPLv3 or any later version. + * Refer to the included LICENSE file. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "widget/debugger/utility.hpp" +#include "tile_viewer.hpp" + +TileViewer::TileViewer(nba::CoreBase* core, QWidget* parent) : QWidget(parent) { + vram = core->GetVRAM(); + pram = (u16*)core->GetPRAM(); + + QHBoxLayout* hbox = new QHBoxLayout{}; + + { + QVBoxLayout* vbox = new QVBoxLayout{}; + + magnification_input = new QSpinBox{}; + magnification_input->setMinimum(1); + magnification_input->setMaximum(16); + vbox->addWidget(magnification_input); + + palette_input = new QSpinBox{}; + palette_input->setMinimum(0); + palette_input->setMaximum(15); + vbox->addWidget(palette_input); + + QGroupBox* tile_base_group_box = new QGroupBox{}; + tile_base_group_box->setTitle(tr("Tile Base")); + + QVBoxLayout* tile_base_vbox = new QVBoxLayout{}; + + for(u32 tile_base = 0x06000000u; tile_base <= 0x06010000u; tile_base += 0x4000u) { + QRadioButton* radio_button = new QRadioButton{ + QString::fromStdString(fmt::format("0x{:08X}", tile_base))}; + + connect(radio_button, &QRadioButton::pressed, [this, tile_base]() { + this->tile_base = tile_base & 0xFFFFFFu; + }); + + tile_base_vbox->addWidget(radio_button); + + if(tile_base == 0x06000000u) radio_button->click(); + } + + tile_base_group_box->setLayout(tile_base_vbox); + + vbox->addWidget(tile_base_group_box); + + hbox->addLayout(vbox); + } + + canvas = new QWidget{}; + canvas->installEventFilter(this); + hbox->addWidget(canvas); + + setLayout(hbox); +} + +bool TileViewer::eventFilter(QObject* object, QEvent* event) { + if(object == canvas && event->type() == QEvent::Paint) { + const int canvas_w = canvas->size().width(); + const int canvas_h = canvas->size().height(); + + const QRect src_rect{0, 0, 256, 256 * canvas_h / canvas_w}; + const QRect dst_rect{0, 0, canvas_w, canvas_h}; + + QPainter painter{canvas}; + painter.drawImage(dst_rect, image_rgb32, src_rect); + return true; + } + + return false; +} + +void TileViewer::Update() { + if(!isVisible()) { + return; + } + + const int magnification = magnification_input->value(); + const int palette_offset = tile_base == 0x10000u ? 256 : 0; + + u32* const buffer = (u32*)image_rgb32.bits(); + + int height = 256; + u32 tile_address = tile_base; + + if(eight_bpp) { + u16* const palette = &pram[palette_offset]; + + for(int tile = 0; tile < 512; tile++) { + const int tile_base_x = (tile % 32) * 8; + const int tile_base_y = (tile / 32) * 8; + + for(int y = 0; y < 8; y++) { + u64 tile_row_data = nba::read(vram, tile_address); + + for(int x = 0; x < 8; x++) { + buffer[(tile_base_y + y) * 256 + tile_base_x + x] = Rgb565ToArgb8888(palette[(u8)tile_row_data]); + tile_row_data >>= 8; + } + + tile_address += sizeof(u64); + } + } + + height /= 2; + } else { + u16* const palette = &pram[palette_input->value() * 16 + palette_offset]; + + for(int tile = 0; tile < 1024; tile++) { + const int tile_base_x = (tile % 32) * 8; + const int tile_base_y = (tile / 32) * 8; + + for(int y = 0; y < 8; y++) { + u32 tile_row_data = nba::read(vram, tile_address); + + for(int x = 0; x < 8; x++) { + buffer[(tile_base_y + y) * 256 + tile_base_x + x] = Rgb565ToArgb8888(palette[tile_row_data & 15]); + tile_row_data >>= 4; + } + + tile_address += sizeof(u32); + } + } + } + + if(tile_base == 0xC000u) { + height /= 2; + } + + canvas->setFixedSize(256 * magnification, height * magnification); + canvas->update(); +} diff --git a/src/platform/qt/src/widget/debugger/ppu/tile_viewer.hpp b/src/platform/qt/src/widget/debugger/ppu/tile_viewer.hpp new file mode 100644 index 00000000..8b58f809 --- /dev/null +++ b/src/platform/qt/src/widget/debugger/ppu/tile_viewer.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024 fleroviux + * + * Licensed under GPLv3 or any later version. + * Refer to the included LICENSE file. + */ + +#pragma once + +#include +#include +#include +#include +#include + +struct TileViewer : QWidget { + TileViewer(nba::CoreBase* core, QWidget* parent = nullptr); + + bool eventFilter(QObject* object, QEvent* event) override; + + void Update(); + +private: + u8* vram; + u16* pram; + QImage image_rgb32{256, 256, QImage::Format_RGB32}; + QSpinBox* palette_input; + QSpinBox* magnification_input; + QWidget* canvas; + + u32 tile_base = 0; + bool eight_bpp = false; + //int palette_index = 0; + + Q_OBJECT +}; diff --git a/src/platform/qt/src/widget/debugger/ppu/tile_viewer_window.cpp b/src/platform/qt/src/widget/debugger/ppu/tile_viewer_window.cpp new file mode 100644 index 00000000..3b8f1a64 --- /dev/null +++ b/src/platform/qt/src/widget/debugger/ppu/tile_viewer_window.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 fleroviux + * + * Licensed under GPLv3 or any later version. + * Refer to the included LICENSE file. + */ + +#include + +#include "tile_viewer_window.hpp" + +TileViewerWindow::TileViewerWindow(nba::CoreBase* core, QWidget* parent) : QDialog(parent) { + const auto vbox = new QVBoxLayout{}; + + tile_viewer = new TileViewer{core, nullptr}; + vbox->addWidget(tile_viewer); + setLayout(vbox); + + setWindowTitle(tr("Tile Viewer")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); +} + +void TileViewerWindow::Update() { + if(!isVisible()) { + return; + } + tile_viewer->Update(); +} \ No newline at end of file diff --git a/src/platform/qt/src/widget/debugger/ppu/tile_viewer_window.hpp b/src/platform/qt/src/widget/debugger/ppu/tile_viewer_window.hpp new file mode 100644 index 00000000..53506df2 --- /dev/null +++ b/src/platform/qt/src/widget/debugger/ppu/tile_viewer_window.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 fleroviux + * + * Licensed under GPLv3 or any later version. + * Refer to the included LICENSE file. + */ + +#pragma once + +#include +#include +#include + +#include "tile_viewer.hpp" + +struct TileViewerWindow : QDialog { + TileViewerWindow(nba::CoreBase* core, QWidget* parent = nullptr); + +public slots: + void Update(); + +private: + TileViewer* tile_viewer; + + Q_OBJECT +}; diff --git a/src/platform/qt/src/widget/main_window.cpp b/src/platform/qt/src/widget/main_window.cpp index fbe8ffc4..ea37bfe8 100644 --- a/src/platform/qt/src/widget/main_window.cpp +++ b/src/platform/qt/src/widget/main_window.cpp @@ -400,6 +400,15 @@ void MainWindow::CreateToolsMenu() { background_viewer_window->show(); }); + connect(tools_menu->addAction(tr("Tile Viewer")), &QAction::triggered, [this]() { + if(!tile_viewer_window) { + tile_viewer_window = new TileViewerWindow{core_not_thread_safe, this}; + connect(screen.get(), &Screen::RequestDraw, tile_viewer_window, &TileViewerWindow::Update); + } + + tile_viewer_window->show(); + }); + connect(tools_menu->addAction(tr("Sprite Viewer")), &QAction::triggered, [this]() { const auto sprite_viewer_window = new SpriteViewerWindow{core_not_thread_safe, this}; connect(screen.get(), &Screen::RequestDraw, sprite_viewer_window, &SpriteViewerWindow::Update); diff --git a/src/platform/qt/src/widget/main_window.hpp b/src/platform/qt/src/widget/main_window.hpp index a6db68fe..3494d734 100644 --- a/src/platform/qt/src/widget/main_window.hpp +++ b/src/platform/qt/src/widget/main_window.hpp @@ -23,11 +23,12 @@ #include #include "widget/debugger/ppu/background_viewer_window.hpp" +#include "widget/debugger/ppu/palette_viewer_window.hpp" +#include "widget/debugger/ppu/sprite_viewer_window.hpp" +#include "widget/debugger/ppu/tile_viewer_window.hpp" #include "widget/controller_manager.hpp" #include "widget/input_window.hpp" -#include "widget/debugger/ppu/palette_viewer_window.hpp" #include "widget/screen.hpp" -#include "widget/debugger/ppu/sprite_viewer_window.hpp" #include "config.hpp" struct MainWindow : QMainWindow { @@ -155,6 +156,7 @@ private slots: PaletteViewerWindow* palette_viewer_window; BackgroundViewerWindow* background_viewer_window; + TileViewerWindow* tile_viewer_window; QString base_window_title;