[nostalgia/studio] Add export option to tilesheet editor

This commit is contained in:
2022-05-25 21:21:28 -05:00
parent 2448bdcc82
commit 0adfaa7901
6 changed files with 103 additions and 31 deletions
+1
View File
@@ -22,6 +22,7 @@ target_link_libraries(
NostalgiaStudio
NostalgiaCore
NostalgiaGlUtils
lodepng
)
#target_compile_definitions(NostalgiaCore-Studio PRIVATE QT_QML_DEBUG)
@@ -3,6 +3,7 @@
*/
#include <imgui.h>
#include <lodepng.h>
#include <nostalgia/core/media.hpp>
#include <nostalgia/geo/point.hpp>
@@ -11,10 +12,29 @@
namespace nostalgia::core {
static ox::Error toPngFile(const ox::String &path, const TileSheet::SubSheet &s, const Palette &pal, int8_t bpp) noexcept {
ox::Vector<uint8_t> pixels;
s.readPixelsTo(&pixels, bpp);
const unsigned rows = s.rows == -1 ? pixels.size() / PixelsPerTile : static_cast<unsigned>(s.rows);
const unsigned cols = s.columns == -1 ? 1 : static_cast<unsigned>(s.columns);
const auto width = cols * TileWidth;
const auto height = rows * TileHeight;
ox::Vector<unsigned char> outData(pixels.size() * 3);
for (auto idx = 0; const auto colorIdx : pixels) {
const auto pt = idxToPt(idx, static_cast<int>(cols));
const auto i = static_cast<unsigned>(pt.y * static_cast<int>(width) + pt.x) * 3;
const auto c = pal.colors[colorIdx];
outData[i + 0] = (red32(c));
outData[i + 1] = (green32(c));
outData[i + 2] = (blue32(c));
++idx;
}
return OxError(lodepng_encode24_file(path.c_str(), outData.data(), width, height));
}
TileSheetEditorImGui::TileSheetEditorImGui(Context *ctx, const ox::String &path): m_tileSheetEditor(ctx, path) {
m_ctx = ctx;
m_itemPath = path;
const auto lastSlash = std::find(m_itemPath.rbegin(), m_itemPath.rend(), '/').offset();
const auto lastSlash = ox::find(m_itemPath.rbegin(), m_itemPath.rend(), '/').offset();
m_itemName = m_itemPath.substr(lastSlash + 1);
// init palette idx
const auto &palPath = model()->palPath();
@@ -41,6 +61,7 @@ const ox::String &TileSheetEditorImGui::itemDisplayName() const noexcept {
}
void TileSheetEditorImGui::exportFile() {
exportSubhseetToPng();
}
void TileSheetEditorImGui::cut() {
@@ -125,9 +146,13 @@ void TileSheetEditorImGui::draw(core::Context*) noexcept {
}
}
ImGui::SameLine();
if (ImGui::Button("Edit", ImVec2(36, btnHeight))) {
if (ImGui::Button("Edit", ImVec2(51, btnHeight))) {
showSubsheetEditor();
}
ImGui::SameLine();
if (ImGui::Button("Export", ImVec2(51, btnHeight))) {
exportSubhseetToPng();
}
TileSheet::SubSheetIdx path;
static constexpr auto flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody;
if (ImGui::BeginTable("Subsheets", 3, flags)) {
@@ -214,6 +239,21 @@ void TileSheetEditorImGui::showSubsheetEditor() noexcept {
}
}
void TileSheetEditorImGui::exportSubhseetToPng() noexcept {
auto [path, err] = studio::saveFile({{"PNG", "png"}});
if (err) {
return;
}
// subsheet to png
const auto &img = model()->img();
const auto &s = *model()->activeSubSheet();
const auto &pal = model()->pal();
err = toPngFile(path, s, pal, img.bpp);
if (err) {
oxErrorf("Tilesheet export failed: {}", toStr(err));
}
}
void TileSheetEditorImGui::drawTileSheet(const geo::Vec2 &fbSize) noexcept {
const auto winPos = ImGui::GetWindowPos();
const auto fbSizei = geo::Size(static_cast<int>(fbSize.x), static_cast<int>(fbSize.y));
@@ -230,10 +270,10 @@ void TileSheetEditorImGui::drawTileSheet(const geo::Vec2 &fbSize) noexcept {
m_tileSheetEditor.draw();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
ImGui::Image(
reinterpret_cast<void*>(m_framebuffer.color.id),
static_cast<ImVec2>(fbSize),
ImVec2(0, 1),
ImVec2(1, 0));
reinterpret_cast<void*>(m_framebuffer.color.id),
static_cast<ImVec2>(fbSize),
ImVec2(0, 1),
ImVec2(1, 0));
// handle input, this must come after drawing
const auto &io = ImGui::GetIO();
const auto mousePos = geo::Vec2(io.MousePos);
@@ -242,7 +282,7 @@ void TileSheetEditorImGui::drawTileSheet(const geo::Vec2 &fbSize) noexcept {
const auto wheelh = io.MouseWheelH;
if (wheel != 0) {
const auto zoomMod = ox::defines::OS == ox::OS::Darwin ?
io.KeySuper : core::buttonDown(m_ctx, core::Key::Mod_Ctrl);
io.KeySuper : core::buttonDown(m_ctx, core::Key::Mod_Ctrl);
m_tileSheetEditor.scrollV(fbSize, wheel, zoomMod);
}
if (wheelh != 0) {
@@ -43,7 +43,7 @@ class TileSheetEditorImGui: public studio::BaseEditor {
};
std::size_t m_selectedPaletteIdx = 0;
Context *m_ctx = nullptr;
ox::Vector<ox::String> m_paletteList;
ox::Vector<ox::String> m_paletteList{};
SubSheetEditor m_subsheetEditor;
ox::String m_itemPath;
ox::String m_itemName;
@@ -87,6 +87,8 @@ class TileSheetEditorImGui: public studio::BaseEditor {
private:
void showSubsheetEditor() noexcept;
void exportSubhseetToPng() noexcept;
[[nodiscard]]
constexpr auto model() const noexcept {
return m_tileSheetEditor.model();
@@ -6,6 +6,7 @@
#include <nostalgia/core/clipboard.hpp>
#include <nostalgia/core/media.hpp>
#include <ox/std/algorithm.hpp>
#include <ox/std/buffer.hpp>
#include <ox/std/memory.hpp>
@@ -119,7 +120,7 @@ class DrawCommand: public TileSheetCommand {
auto &subsheet = m_img->getSubSheet(m_subSheetIdx);
if (m_changes.back().value.idx != idx && subsheet.getPixel(m_img->bpp, idx) != m_palIdx) {
// duplicate entries are bad
auto existing = std::find_if(m_changes.cbegin(), m_changes.cend(), [idx](const auto &c) {
auto existing = ox::find_if(m_changes.cbegin(), m_changes.cend(), [idx](const auto &c) {
return c.idx == idx;
});
if (existing == m_changes.cend()) {
@@ -501,7 +502,7 @@ class PaletteChangeCommand: public TileSheetCommand {
ox::FileAddress m_newPalette;
public:
constexpr PaletteChangeCommand(const TileSheet::SubSheetIdx &idx, TileSheet *img, const ox::String &newPalette) noexcept {
PaletteChangeCommand(const TileSheet::SubSheetIdx &idx, TileSheet *img, const ox::String &newPalette) noexcept {
m_idx = idx;
m_img = img;
m_oldPalette = m_img->defaultPalette;
@@ -73,16 +73,19 @@ class TileSheetEditorModel: public ox::SignalHandler {
void setActiveSubsheet(const TileSheet::SubSheetIdx&) noexcept;
[[nodiscard]]
constexpr const TileSheet::SubSheet *activeSubSheet() const noexcept {
auto &activeSubSheet = m_img.getSubSheet(m_activeSubsSheetIdx);
return &activeSubSheet;
}
[[nodiscard]]
constexpr TileSheet::SubSheet *activeSubSheet() noexcept {
auto &activeSubSheet = m_img.getSubSheet(m_activeSubsSheetIdx);
return &activeSubSheet;
}
[[nodiscard]]
constexpr const TileSheet::SubSheetIdx &activeSubSheetIdx() const noexcept {
return m_activeSubsSheetIdx;
}
@@ -106,6 +109,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
ox::Error saveFile() noexcept;
[[nodiscard]]
constexpr studio::UndoStack *undoStack() noexcept;
bool pixelSelected(std::size_t idx) const noexcept;