From 8f21670439e51923258a31175797f9d137d774fa Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Tue, 26 Nov 2019 23:23:12 -0600 Subject: [PATCH] [nostalgia/core/studio] Start tile sheet editor --- src/nostalgia/core/qt/gfx.cpp | 9 +- src/nostalgia/core/studio/CMakeLists.txt | 6 +- src/nostalgia/core/studio/Pixel.qml | 22 +++ src/nostalgia/core/studio/Tile.qml | 36 +++++ src/nostalgia/core/studio/TileSheetEditor.qml | 28 ++++ src/nostalgia/core/studio/consts.hpp | 15 ++ src/nostalgia/core/studio/imgconv.cpp | 4 +- .../core/studio/import_tilesheet_wizard.cpp | 12 +- src/nostalgia/core/studio/plugin.cpp | 15 +- src/nostalgia/core/studio/plugin.hpp | 5 +- src/nostalgia/core/studio/rsrc.qrc | 7 + src/nostalgia/core/studio/tilesheeteditor.cpp | 131 ++++++++++++++++++ src/nostalgia/core/studio/tilesheeteditor.hpp | 63 +++++++++ 13 files changed, 334 insertions(+), 19 deletions(-) create mode 100644 src/nostalgia/core/studio/Pixel.qml create mode 100644 src/nostalgia/core/studio/Tile.qml create mode 100644 src/nostalgia/core/studio/TileSheetEditor.qml create mode 100644 src/nostalgia/core/studio/consts.hpp create mode 100644 src/nostalgia/core/studio/rsrc.qrc create mode 100644 src/nostalgia/core/studio/tilesheeteditor.cpp create mode 100644 src/nostalgia/core/studio/tilesheeteditor.hpp diff --git a/src/nostalgia/core/qt/gfx.cpp b/src/nostalgia/core/qt/gfx.cpp index 7eaf932d..90b9707a 100644 --- a/src/nostalgia/core/qt/gfx.cpp +++ b/src/nostalgia/core/qt/gfx.cpp @@ -18,13 +18,14 @@ ox::Error initConsole(Context*) { return OxError(1); } -ox::Error loadTileSheet(Context*, InodeId_t) { +ox::Error loadTileSheet(Context*, + TileSheetSpace, + int, + ox::FileAddress, + ox::FileAddress) { return OxError(1); } -void puts(Context*, int, const char*) { -} - void setTile(Context*, int, int, int, uint8_t) { } diff --git a/src/nostalgia/core/studio/CMakeLists.txt b/src/nostalgia/core/studio/CMakeLists.txt index 570c56e2..b2f78841 100644 --- a/src/nostalgia/core/studio/CMakeLists.txt +++ b/src/nostalgia/core/studio/CMakeLists.txt @@ -1,21 +1,25 @@ - add_library( NostalgiaCore-Studio SHARED imgconv.cpp import_tilesheet_wizard.cpp new_tilesheet_wizard.cpp plugin.cpp + tilesheeteditor.cpp + rsrc.qrc ) target_link_libraries( NostalgiaCore-Studio Qt5::Core Qt5::Widgets + Qt5::QuickWidgets NostalgiaStudio OxFS OxStd ) +target_compile_definitions(NostalgiaCore-Studio PRIVATE QT_QML_DEBUG) + install( TARGETS NostalgiaCore-Studio diff --git a/src/nostalgia/core/studio/Pixel.qml b/src/nostalgia/core/studio/Pixel.qml new file mode 100644 index 00000000..369e88cb --- /dev/null +++ b/src/nostalgia/core/studio/Pixel.qml @@ -0,0 +1,22 @@ + +import QtQuick 2.0 + +Rectangle { + id: pixel; + property int pixelNumber: index + color: sheetData.pixel(index) + width: parent.width / 8 + height: parent.height / 8 + border.color: '#717d7e' + border.width: 1 + + Text { + text: (index % 8 + 1) + ', ' + Math.floor(index / 8 + 1) + font.family: 'Helvetica' + font.pointSize: 11 + color: '#717d7e88' + visible: pixel.width > 42 // that's acutally not HGtG reference + anchors.horizontalCenter: pixel.horizontalCenter + anchors.bottom: pixel.bottom + } +} diff --git a/src/nostalgia/core/studio/Tile.qml b/src/nostalgia/core/studio/Tile.qml new file mode 100644 index 00000000..84423e63 --- /dev/null +++ b/src/nostalgia/core/studio/Tile.qml @@ -0,0 +1,36 @@ + +import QtQuick 2.0 + +Rectangle { + id: tile; + property int tileNumber: -1 + width: parent.width + height: parent.height + x: parent.width / 2 - tile.width / 2 + y: parent.height / 2 - tile.height / 2 + color: '#000000' + + Grid { + width: tile.width + height: tile.height + rows: 8 + columns: 8 + Repeater { + model: 64 + Pixel { + pixelNumber: index + } + } + } + + // place an outline Rectangle above the pixels + Rectangle { + width: parent.width + height: parent.height + // make fill transparent + color: '#00000000' + border.color: '#000000' + border.width: 2 + } + +} diff --git a/src/nostalgia/core/studio/TileSheetEditor.qml b/src/nostalgia/core/studio/TileSheetEditor.qml new file mode 100644 index 00000000..89de1a67 --- /dev/null +++ b/src/nostalgia/core/studio/TileSheetEditor.qml @@ -0,0 +1,28 @@ + +import QtQuick 2.0 +import 'qrc:/qml/' + +Rectangle { + id: tileSheetEditor + color: '#717d7e' + + Grid { + id: tileGrid + property int baseTileSize: Math.min(parent.width / tileGrid.columns, parent.height / tileGrid.rows) + width: tileGrid.columns * tileGrid.baseTileSize * 0.85 + height: tileGrid.rows * tileGrid.baseTileSize * 0.85 + anchors.horizontalCenter: tileSheetEditor.horizontalCenter + anchors.verticalCenter: tileSheetEditor.verticalCenter + rows: 2 + columns: 3 + Repeater { + model: tileGrid.rows * tileGrid.columns + Tile { + tileNumber: index + width: tileGrid.width / tileGrid.columns + height: tileGrid.height / tileGrid.rows + } + } + } + +} diff --git a/src/nostalgia/core/studio/consts.hpp b/src/nostalgia/core/studio/consts.hpp new file mode 100644 index 00000000..e6b22e50 --- /dev/null +++ b/src/nostalgia/core/studio/consts.hpp @@ -0,0 +1,15 @@ +/* + * Copyright 2016 - 2019 gtalent2@gmail.com + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +namespace nostalgia::core { + +constexpr auto PluginName = "NostalgiaCore"; + +} diff --git a/src/nostalgia/core/studio/imgconv.cpp b/src/nostalgia/core/studio/imgconv.cpp index 6258d30b..75f4a90a 100644 --- a/src/nostalgia/core/studio/imgconv.cpp +++ b/src/nostalgia/core/studio/imgconv.cpp @@ -26,7 +26,7 @@ namespace { } [[nodiscard]] int pointToIdx(int w, int x, int y) { - const auto colLength = 64; + constexpr auto colLength = 64; const auto rowLength = (w / 8) * colLength; const auto colStart = colLength * (x / 8); const auto rowStart = rowLength * (y / 8); @@ -87,7 +87,7 @@ namespace { for (int x = 0; x < src.width(); x++) { for (int y = 0; y < src.height(); y++) { auto destI = pointToIdx(src.width(), x, y); - if (destI <= argTiles * 64) { + if (destI < argTiles * 64) { const auto c = src.pixel(x, y); // assign color a color id for the palette if (!colors.contains(c)) { diff --git a/src/nostalgia/core/studio/import_tilesheet_wizard.cpp b/src/nostalgia/core/studio/import_tilesheet_wizard.cpp index 20228429..f0ff7fb1 100644 --- a/src/nostalgia/core/studio/import_tilesheet_wizard.cpp +++ b/src/nostalgia/core/studio/import_tilesheet_wizard.cpp @@ -74,14 +74,12 @@ int ImportTilesheetWizardPalettePage::accept() { const auto outPath = PaletteDir + paletteName + FileExt_npal; core::NostalgiaPalette pal; pal = std::move(ng->pal); - auto [buff, err] = toBuffer(&pal); - oxReturnError(err); - oxReturnError(m_ctx->project->write(outPath, buff.data(), buff.size())); + m_ctx->project->writeObj(outPath, &pal); + auto defaultPalette = outPath.toUtf8(); + ng->defaultPalette = defaultPalette.data(); } - auto [buff, err] = toBuffer(ng.get()); - oxReturnError(err); - oxReturnError(m_ctx->project->write(outPath, buff.data(), buff.size())); - return m_ctx->project->saveRomFs(); + m_ctx->project->writeObj(outPath, ng.get()); + return 0; } } diff --git a/src/nostalgia/core/studio/plugin.cpp b/src/nostalgia/core/studio/plugin.cpp index ca9f88ec..c4b34742 100644 --- a/src/nostalgia/core/studio/plugin.cpp +++ b/src/nostalgia/core/studio/plugin.cpp @@ -8,6 +8,7 @@ #include "new_tilesheet_wizard.hpp" #include "import_tilesheet_wizard.hpp" +#include "tilesheeteditor.hpp" #include "plugin.hpp" @@ -15,9 +16,6 @@ using namespace nostalgia::studio; namespace nostalgia::core { -Plugin::Plugin() { -} - QVector Plugin::newWizards(const Context *ctx) { return { { @@ -45,4 +43,15 @@ QVector Plugin::importWizards(const studio::Context *ctx) { }; } +QVector Plugin::editors(const studio::Context *ctx) { + return { + { + {"ng"}, + [ctx](QString path) { + return new TileSheetEditor(path, ctx, ctx->tabParent); + } + } + }; +} + } diff --git a/src/nostalgia/core/studio/plugin.hpp b/src/nostalgia/core/studio/plugin.hpp index 8d3db8e2..36ce36bf 100644 --- a/src/nostalgia/core/studio/plugin.hpp +++ b/src/nostalgia/core/studio/plugin.hpp @@ -20,11 +20,12 @@ class Plugin: public QObject, studio::Plugin { Q_INTERFACES(nostalgia::studio::Plugin) public: - Plugin(); - QVector newWizards(const studio::Context *ctx) override; QVector importWizards(const studio::Context *args) override; + + QVector editors(const studio::Context *ctx) override; + }; } diff --git a/src/nostalgia/core/studio/rsrc.qrc b/src/nostalgia/core/studio/rsrc.qrc new file mode 100644 index 00000000..f8e0dd71 --- /dev/null +++ b/src/nostalgia/core/studio/rsrc.qrc @@ -0,0 +1,7 @@ + + + Pixel.qml + Tile.qml + TileSheetEditor.qml + + diff --git a/src/nostalgia/core/studio/tilesheeteditor.cpp b/src/nostalgia/core/studio/tilesheeteditor.cpp new file mode 100644 index 00000000..f0c1666a --- /dev/null +++ b/src/nostalgia/core/studio/tilesheeteditor.cpp @@ -0,0 +1,131 @@ +/* + * Copyright 2016 - 2019 gtalent2@gmail.com + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include +#include +#include +#include + +#include + +#include "consts.hpp" +#include "tilesheeteditor.hpp" + +namespace nostalgia::core { + +uint32_t toColor32(Color nc) { + auto r = ((nc & 0b0000000000011111) >> 0) * 8; + auto g = ((nc & 0b0000001111100000) >> 5) * 8; + auto b = ((nc & 0b0111110000000000) >> 10) * 8; + auto a = 255; + return a | (b << 8) | (g << 16) | (r << 24); +} + +[[nodiscard]] +QVector toPixels(const NostalgiaGraphic *ng, const NostalgiaPalette *npal) { + if (!npal) { + npal = &ng->pal; + } + + QVector out; + out.reserve(ng->tiles.size() * sizeof(Color)); + + if (ng->bpp == 8) { + for (std::size_t i = 0; i < ng->tiles.size(); i++) { + auto p = ng->tiles[i]; + auto c = p < npal->colors.size() ? npal->colors[p] : 0; + out.push_back(toColor32(c)); + } + } else { + for (std::size_t i = 0; i < ng->tiles.size() * 2; i++) { + uint8_t p; + if (i & 1) { + p = ng->tiles[i / 2] >> 4; + } else { + p = ng->tiles[i / 2] & 0xF; + } + auto c = p < npal->colors.size() ? npal->colors[p] : 0; + out.push_back(toColor32(c)); + } + } + + return out; +} + +[[nodiscard]] +QVector toPixels(const studio::Context *ctx, QString ngPath, QString palPath = "") { + auto ng = ctx->project->loadObj(ngPath); + std::unique_ptr npal; + if (palPath == "" && ng->defaultPalette.type() == ox::FileAddressType::Path) { + palPath = ng->defaultPalette.getPath().value; + } + try { + npal = ctx->project->loadObj(palPath); + qInfo() << "Opened palette" << palPath; + } catch (ox::Error) { + qWarning() << "Could not open palette" << palPath; + } + return toPixels(ng.get(), npal.get()); +} + + +QString SheetData::pixel(int index) { + return "#" + QString("%1").arg(QString::number(m_pixels[index], 16), 8, '0'); +} + +void SheetData::updatePixels(const studio::Context *ctx, QString path) { + m_pixels = toPixels(ctx, path); +} + + +TileSheetEditor::TileSheetEditor(QString path, const studio::Context *ctx, QWidget *parent): QWidget(parent) { + m_ctx = ctx; + auto lyt = new QVBoxLayout(this); + m_splitter = new QSplitter(this); + auto canvas = new QQuickWidget(m_splitter); + lyt->addWidget(m_splitter); + m_splitter->addWidget(canvas); + m_splitter->addWidget(setupColorPicker(m_splitter)); + m_splitter->setStretchFactor(0, 1); + m_sheetData.updatePixels(m_ctx, path); + canvas->rootContext()->setContextProperty("sheetData", &m_sheetData); + canvas->setSource(QUrl::fromLocalFile(":/qml/TileSheetEditor.qml")); + canvas->setResizeMode(QQuickWidget::SizeRootObjectToView); + restoreState(); +} + +TileSheetEditor::~TileSheetEditor() { + saveState(); +} + +QWidget *TileSheetEditor::setupColorPicker(QWidget *parent) { + auto colorPicker = new QWidget(parent); + auto lyt = new QVBoxLayout(colorPicker); + m_colorPicker.palette = new QComboBox(colorPicker); + m_colorPicker.colorTable = new QTableWidget(colorPicker); + lyt->addWidget(m_colorPicker.palette); + lyt->addWidget(m_colorPicker.colorTable); + return colorPicker; +} + +void TileSheetEditor::saveState() { + QSettings settings(m_ctx->orgName, PluginName); + settings.beginGroup("TileSheetEditor/state"); + settings.setValue("m_splitter/state", m_splitter->saveState()); + settings.endGroup(); +} + +void TileSheetEditor::restoreState() { + QSettings settings(m_ctx->orgName, PluginName); + settings.beginGroup("TileSheetEditor/state"); + m_splitter->restoreState(settings.value("m_splitter/state").toByteArray()); + settings.endGroup(); +} + +} diff --git a/src/nostalgia/core/studio/tilesheeteditor.hpp b/src/nostalgia/core/studio/tilesheeteditor.hpp new file mode 100644 index 00000000..c2225283 --- /dev/null +++ b/src/nostalgia/core/studio/tilesheeteditor.hpp @@ -0,0 +1,63 @@ +/* + * Copyright 2016 - 2019 gtalent2@gmail.com + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include +#include +#include +#include + +#include + +namespace nostalgia::core { + +class SheetData: public QObject { + Q_OBJECT + + private: + QVector m_pixels; + + public: + Q_INVOKABLE QString pixel(int index); + + void updatePixels(const studio::Context *ctx, QString path); + + signals: + void refreshTileSheet(); + +}; + + +class TileSheetEditor: public QWidget { + Q_OBJECT + + private: + const studio::Context *m_ctx = nullptr; + SheetData m_sheetData; + QSplitter *m_splitter = nullptr; + struct { + QComboBox *palette = nullptr; + QTableWidget *colorTable = nullptr; + } m_colorPicker; + + public: + TileSheetEditor(QString path, const studio::Context *ctx, QWidget *parent); + + virtual ~TileSheetEditor(); + + private: + QWidget *setupColorPicker(QWidget *widget); + + void saveState(); + + void restoreState(); + +}; + +}