Merge commit 'ab760b064fd6a302bad13274e0e02b2b2c957b67'

This commit is contained in:
2025-01-26 15:42:50 -06:00
40 changed files with 810 additions and 193 deletions

View File

@@ -414,6 +414,10 @@ void setPixel(TileSheet::SubSheet &ss, ox::Point const&pt, uint8_t palIdx) noexc
ox::Error setPixelCount(TileSheet::SubSheet &ss, std::size_t cnt) noexcept;
void flipX(TileSheet::SubSheet &ss, ox::Point const &a, ox::Point const &b) noexcept;
void flipY(TileSheet::SubSheet &ss, ox::Point const &a, ox::Point const &b) noexcept;
/**
* Gets a count of the pixels in this sheet, and not that of its children.
* @return a count of the pixels in this sheet

View File

@@ -9,9 +9,10 @@
namespace nostalgia::gfx {
MoveColorCommand::MoveColorCommand(
Palette &pal, size_t page, size_t srcIdx, size_t dstIdx) noexcept:
Palette &pal,
size_t const srcIdx,
size_t const dstIdx) noexcept:
m_pal(pal),
m_page(page),
m_srcIdx(srcIdx),
m_dstIdx(dstIdx) {}
@@ -29,10 +30,18 @@ ox::Error MoveColorCommand::undo() noexcept {
return {};
}
void MoveColorCommand::moveColor(size_t srcIdx, size_t dstIdx) noexcept {
auto const c = color(m_pal, m_page, srcIdx);
std::ignore = colors(m_pal, m_page).erase(srcIdx);
colors(m_pal, m_page).emplace(dstIdx, c);
void MoveColorCommand::moveColor(
size_t const srcIdx, size_t const dstIdx) noexcept {
for (size_t page{}; page < m_pal.pages.size(); ++page) {
auto const c = color(m_pal, page, srcIdx);
std::ignore = colors(m_pal, page).erase(srcIdx);
colors(m_pal, page).emplace(dstIdx, c);
}
{
auto name = std::move(m_pal.colorNames[srcIdx]);
std::ignore = m_pal.colorNames.erase(srcIdx);
m_pal.colorNames.emplace(dstIdx, std::move(name));
}
}
}

View File

@@ -13,12 +13,11 @@ namespace nostalgia::gfx {
class MoveColorCommand: public studio::UndoCommand {
private:
Palette &m_pal;
size_t const m_page = 0;
std::size_t const m_srcIdx = 0;
std::size_t const m_dstIdx = 0;
public:
MoveColorCommand(Palette &pal, size_t page, size_t srcIdx, size_t dstIdx) noexcept;
MoveColorCommand(Palette &pal, size_t srcIdx, size_t dstIdx) noexcept;
~MoveColorCommand() noexcept override = default;

View File

@@ -23,6 +23,15 @@ namespace nostalgia::gfx {
namespace ig = studio::ig;
struct ColorDragDrop {
static constexpr auto TypeName = "nostalgia.gfx.ColorDragDrop";
static constexpr auto TypeVersion = 1;
uint32_t i{};
};
OX_MODEL_BEGIN(ColorDragDrop)
OX_MODEL_FIELD(i)
OX_MODEL_END()
void PaletteEditorImGui::PageRenameDialog::draw(turbine::Context &tctx) noexcept {
if (!m_show) {
@@ -45,7 +54,7 @@ void PaletteEditorImGui::PageRenameDialog::draw(turbine::Context &tctx) noexcept
PaletteEditorImGui::PaletteEditorImGui(studio::StudioContext &sctx, ox::StringParam path):
Editor(std::move(path)),
Editor(sctx, std::move(path)),
m_sctx(sctx),
m_tctx(sctx.tctx),
m_pal(*keel::readObj<Palette>(keelCtx(m_tctx), itemPath()).unwrapThrow()) {
@@ -125,26 +134,6 @@ void PaletteEditorImGui::drawColorsEditor() noexcept {
m_selectedColorRow = ox::min(colorCnt(m_pal, m_page) - 1, m_selectedColorRow);
colorEditor = m_selectedColorRow < colorCnt(m_pal, m_page);
}
ImGui::SameLine();
ImGui::BeginDisabled(m_selectedColorRow <= 0);
{
if (ImGui::Button("Move Up", sz)) {
std::ignore = pushCommand<MoveColorCommand>(
m_pal, m_page, m_selectedColorRow, m_selectedColorRow - 1);
--m_selectedColorRow;
}
}
ImGui::EndDisabled();
ImGui::SameLine();
ImGui::BeginDisabled(m_selectedColorRow >= colorCnt(m_pal, m_page) - 1);
{
if (ImGui::Button("Move Down", sz)) {
std::ignore = pushCommand<MoveColorCommand>(
m_pal, m_page, m_selectedColorRow, m_selectedColorRow + 1);
++m_selectedColorRow;
}
}
ImGui::EndDisabled();
}
ImGui::EndDisabled();
}
@@ -179,6 +168,16 @@ void PaletteEditorImGui::drawColorsEditor() noexcept {
"##ColorRow", i == m_selectedColorRow, ImGuiSelectableFlags_SpanAllColumns)) {
m_selectedColorRow = i;
}
std::ignore = ig::dragDropSource([this, i] {
ImGui::Text("%s", m_pal.colorNames[i].c_str());
return ig::setDragDropPayload(ColorDragDrop{i});
}, ImGuiDragDropFlags_SourceAllowNullID);
if (ig::DragDropTarget const d; d) {
auto const [src, err] = ig::getDragDropPayload<ColorDragDrop>();
if (!err) {
std::ignore = pushCommand<MoveColorCommand>(m_pal, src.i, i);
}
}
++i;
}
}

View File

@@ -4,6 +4,7 @@ target_sources(
cutpastecommand.cpp
deletetilescommand.cpp
drawcommand.cpp
flipcommand.cpp
inserttilescommand.cpp
palettechangecommand.cpp
rmsubsheetcommand.cpp

View File

@@ -12,14 +12,16 @@ namespace nostalgia::gfx {
// Command IDs to use with QUndoCommand::id()
enum class CommandId {
Draw = 1,
AddSubSheet = 2,
RmSubSheet = 3,
DeleteTile = 4,
InsertTile = 4,
UpdateSubSheet = 5,
Cut = 6,
Paste = 7,
PaletteChange = 8,
AddSubSheet,
RmSubSheet,
DeleteTile,
FlipXCommand,
FlipYCommand,
InsertTile,
UpdateSubSheet,
Cut,
Paste,
PaletteChange,
};
constexpr bool operator==(CommandId c, int i) noexcept {

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include "flipcommand.hpp"
#include <utility>
namespace nostalgia::gfx {
FlipXCommand::FlipXCommand(
TileSheet &img,
TileSheet::SubSheetIdx subSheetIdx,
ox::Point const &a,
ox::Point const &b) noexcept:
m_img{img},
m_subSheetIdx{std::move(subSheetIdx)},
m_ptA{a},
m_ptB{b} {}
ox::Error FlipXCommand::redo() noexcept {
auto &ss = getSubSheet(m_img, m_subSheetIdx);
flipX(ss, m_ptA, m_ptB);
return {};
}
ox::Error FlipXCommand::undo() noexcept {
auto &ss = getSubSheet(m_img, m_subSheetIdx);
flipX(ss, m_ptA, m_ptB);
return {};
}
int FlipXCommand::commandId() const noexcept {
return static_cast<int>(CommandId::FlipXCommand);
}
TileSheet::SubSheetIdx const &FlipXCommand::subsheetIdx() const noexcept {
return m_subSheetIdx;
}
FlipYCommand::FlipYCommand(
TileSheet &img,
TileSheet::SubSheetIdx subSheetIdx,
ox::Point const &a,
ox::Point const &b) noexcept:
m_img{img},
m_subSheetIdx{std::move(subSheetIdx)},
m_ptA{a},
m_ptB{b} {}
ox::Error FlipYCommand::redo() noexcept {
auto &ss = getSubSheet(m_img, m_subSheetIdx);
flipY(ss, m_ptA, m_ptB);
return {};
}
ox::Error FlipYCommand::undo() noexcept {
auto &ss = getSubSheet(m_img, m_subSheetIdx);
flipY(ss, m_ptA, m_ptB);
return {};
}
int FlipYCommand::commandId() const noexcept {
return static_cast<int>(CommandId::FlipYCommand);
}
TileSheet::SubSheetIdx const &FlipYCommand::subsheetIdx() const noexcept {
return m_subSheetIdx;
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include "commands.hpp"
namespace nostalgia::gfx {
class FlipXCommand: public TileSheetCommand {
private:
TileSheet &m_img;
TileSheet::SubSheetIdx m_subSheetIdx;
ox::Point const m_ptA;
ox::Point const m_ptB;
public:
FlipXCommand(
TileSheet &img,
TileSheet::SubSheetIdx subSheetIdx,
ox::Point const &a,
ox::Point const &b) noexcept;
ox::Error redo() noexcept final;
ox::Error undo() noexcept final;
[[nodiscard]]
int commandId() const noexcept final;
[[nodiscard]]
TileSheet::SubSheetIdx const&subsheetIdx() const noexcept override;
};
class FlipYCommand: public TileSheetCommand {
private:
TileSheet &m_img;
TileSheet::SubSheetIdx m_subSheetIdx;
ox::Point const m_ptA;
ox::Point const m_ptB;
public:
FlipYCommand(
TileSheet &img,
TileSheet::SubSheetIdx subSheetIdx,
ox::Point const &a,
ox::Point const &b) noexcept;
ox::Error redo() noexcept final;
ox::Error undo() noexcept final;
[[nodiscard]]
int commandId() const noexcept final;
[[nodiscard]]
TileSheet::SubSheetIdx const&subsheetIdx() const noexcept override;
};
}

View File

@@ -70,8 +70,8 @@ static ox::Error toPngFile(
ox::Vector<uint32_t> &&pixels,
Palette const&pal,
size_t page,
unsigned width,
unsigned height) noexcept {
unsigned const width,
unsigned const height) noexcept {
for (auto &c : pixels) {
c = color32(color(pal, page, c)) | static_cast<Color32>(0XFF << 24);
}
@@ -87,7 +87,7 @@ static ox::Error toPngFile(
}
TileSheetEditorImGui::TileSheetEditorImGui(studio::StudioContext &sctx, ox::StringParam path):
Editor(std::move(path)),
Editor(sctx, std::move(path)),
m_sctx{sctx},
m_tctx{m_sctx.tctx},
m_palPicker{"Palette Chooser", keelCtx(sctx), FileExt_npal},
@@ -96,7 +96,6 @@ TileSheetEditorImGui::TileSheetEditorImGui(studio::StudioContext &sctx, ox::Stri
// connect signal/slots
m_subsheetEditor.inputSubmitted.connect(this, &TileSheetEditorImGui::updateActiveSubsheet);
m_exportMenu.inputSubmitted.connect(this, &TileSheetEditorImGui::exportSubhseetToPng);
m_model.paletteChanged.connect(this, &TileSheetEditorImGui::setPaletteSelection);
// load config
auto const&config = studio::readConfig<TileSheetEditorConfig>(
keelCtx(m_sctx), itemPath());
@@ -125,7 +124,7 @@ bool TileSheetEditorImGui::acceptsClipboardPayload() const noexcept {
return m_model.acceptsClipboardPayload();
}
void TileSheetEditorImGui::keyStateChanged(turbine::Key key, bool down) {
void TileSheetEditorImGui::keyStateChanged(turbine::Key const key, bool const down) {
if (!down) {
return;
}
@@ -223,7 +222,19 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept {
}
}
ImGui::EndChild();
auto const ySize = controlsSize.y - 38;
ImGui::BeginChild("OperationsBox", {s_palViewWidth - 24, ig::BtnSz.y + 17}, true);
{
auto constexpr btnSz = ImVec2{55, ig::BtnSz.y};
if (ig::PushButton("Flip X", btnSz)) {
oxLogError(m_model.flipX());
}
ImGui::SameLine();
if (ig::PushButton("Flip Y", btnSz)) {
oxLogError(m_model.flipY());
}
}
ImGui::EndChild();
auto const ySize = controlsSize.y - (38 + ig::BtnSz.y + 21);
// draw palette/color picker
ImGui::BeginChild("Palette", {s_palViewWidth - 24, ySize / 2.f}, true);
{
@@ -526,14 +537,11 @@ void TileSheetEditorImGui::drawPaletteMenu() noexcept {
}
}
ox::Error TileSheetEditorImGui::updateActiveSubsheet(ox::StringView const&name, int cols, int rows) noexcept {
ox::Error TileSheetEditorImGui::updateActiveSubsheet(
ox::StringView const&name, int const cols, int const rows) noexcept {
return m_model.updateSubsheet(m_model.activeSubSheetIdx(), name, cols, rows);
}
ox::Error TileSheetEditorImGui::setPaletteSelection() noexcept {
return {};
}
void TileSheetEditorImGui::setActiveSubsheet(TileSheet::SubSheetIdx path) noexcept {
m_model.setActiveSubsheet(path);
studio::editConfig<TileSheetEditorConfig>(keelCtx(m_sctx), itemPath(),

View File

@@ -4,7 +4,6 @@
#pragma once
#include <ox/model/def.hpp>
#include <ox/std/vec.hpp>
#include <glutils/glutils.hpp>
@@ -29,7 +28,7 @@ class TileSheetEditorImGui: public studio::Editor {
public:
ox::Signal<ox::Error(ox::StringViewCR name, int cols, int rows)> inputSubmitted;
void show(ox::StringViewCR name, int cols, int rows) noexcept;
void draw(turbine::Context &sctx) noexcept;
void draw(turbine::Context &tctx) noexcept;
void close() noexcept;
[[nodiscard]]
constexpr bool isOpen() const noexcept { return m_show; }
@@ -41,7 +40,7 @@ class TileSheetEditorImGui: public studio::Editor {
public:
ox::Signal<ox::Error(int scale)> inputSubmitted;
void show() noexcept;
void draw(turbine::Context &sctx) noexcept;
void draw(turbine::Context &tctx) noexcept;
void close() noexcept;
[[nodiscard]]
constexpr bool isOpen() const noexcept { return m_show; }
@@ -99,8 +98,6 @@ class TileSheetEditorImGui: public studio::Editor {
ox::Error updateActiveSubsheet(ox::StringView const&name, int cols, int rows) noexcept;
ox::Error setPaletteSelection() noexcept;
// slots
private:
void setActiveSubsheet(TileSheet::SubSheetIdx path) noexcept;

View File

@@ -22,6 +22,8 @@
#include "commands/updatesubsheetcommand.hpp"
#include "tilesheeteditormodel.hpp"
#include "commands/flipcommand.hpp"
namespace nostalgia::gfx {
// delete pixels of all non-leaf nodes
@@ -42,17 +44,19 @@ Palette const TileSheetEditorModel::s_defaultPalette = {
};
TileSheetEditorModel::TileSheetEditorModel(
studio::StudioContext &sctx, ox::StringViewCR path, studio::UndoStack &undoStack):
m_sctx(sctx),
m_tctx(m_sctx.tctx),
m_path(path),
m_img(*readObj<TileSheet>(keelCtx(m_tctx), m_path).unwrapThrow()),
// ignore failure to load palette
m_pal(readObj<Palette>(keelCtx(m_tctx), m_img.defaultPalette).value),
m_undoStack(undoStack) {
studio::StudioContext &sctx, ox::StringParam path, studio::UndoStack &undoStack):
m_sctx(sctx),
m_tctx(m_sctx.tctx),
m_path(std::move(path)),
m_img(*readObj<TileSheet>(keelCtx(m_tctx), m_path).unwrapThrow()),
// ignore failure to load palette
m_pal(readObj<Palette>(keelCtx(m_tctx), m_img.defaultPalette).value),
m_undoStack(undoStack) {
normalizeSubsheets(m_img.subsheet);
m_pal.updated.connect(this, &TileSheetEditorModel::markUpdated);
m_undoStack.changeTriggered.connect(this, &TileSheetEditorModel::markUpdatedCmdId);
setPalPath();
m_sctx.project->fileMoved.connect(this, &TileSheetEditorModel::handleFileRename);
}
void TileSheetEditorModel::cut() {
@@ -73,7 +77,7 @@ void TileSheetEditorModel::cut() {
auto const pt1 = m_selection->a;
auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight};
turbine::setClipboardObject(m_tctx, std::move(cb));
pushCommand(ox::make<CutPasteCommand>(
std::ignore = pushCommand(ox::make<CutPasteCommand>(
CommandId::Cut, m_img, m_activeSubsSheetIdx, pt1, pt2, blankCb));
}
@@ -106,7 +110,7 @@ void TileSheetEditorModel::paste() {
auto const&s = activeSubSheet();
auto const pt1 = m_selection->a;
auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight};
pushCommand(ox::make<CutPasteCommand>(
std::ignore = pushCommand(ox::make<CutPasteCommand>(
CommandId::Paste, m_img, m_activeSubsSheetIdx, pt1, pt2, *cb));
}
@@ -116,23 +120,12 @@ bool TileSheetEditorModel::acceptsClipboardPayload() const noexcept {
}
ox::StringView TileSheetEditorModel::palPath() const noexcept {
auto &path = m_img.defaultPalette;
constexpr ox::StringView uuidPrefix = "uuid://";
if (ox::beginsWith(path, uuidPrefix)) {
auto const uuid = substr(path, uuidPrefix.bytes());
auto const out = keelCtx(m_tctx).uuidToPath.at(uuid);
if (out.error) {
return {};
}
return *out.value;
} else {
return path;
}
return m_palPath;
}
ox::Error TileSheetEditorModel::setPalette(ox::StringViewCR path) noexcept {
OX_REQUIRE(uuid, keelCtx(m_tctx).pathToUuid.at(path));
pushCommand(ox::make<PaletteChangeCommand>(
std::ignore = pushCommand(ox::make<PaletteChangeCommand>(
activeSubSheetIdx(), m_img, uuid->toString()));
return {};
}
@@ -155,7 +148,7 @@ void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t const pal
if (m_ongoingDrawCommand) {
m_updated = m_updated || m_ongoingDrawCommand->append(idx);
} else if (getPixel(activeSubSheet, idx) != palIdx) {
pushCommand(ox::make<DrawCommand>(
std::ignore = pushCommand(ox::make<DrawCommand>(
m_img, m_activeSubsSheetIdx, idx, static_cast<int>(palIdx)));
}
}
@@ -165,27 +158,27 @@ void TileSheetEditorModel::endDrawCommand() noexcept {
}
void TileSheetEditorModel::addSubsheet(TileSheet::SubSheetIdx const&parentIdx) noexcept {
pushCommand(ox::make<AddSubSheetCommand>(m_img, parentIdx));
std::ignore = pushCommand(ox::make<AddSubSheetCommand>(m_img, parentIdx));
}
void TileSheetEditorModel::rmSubsheet(TileSheet::SubSheetIdx const&idx) noexcept {
pushCommand(ox::make<RmSubSheetCommand>(m_img, idx));
std::ignore = pushCommand(ox::make<RmSubSheetCommand>(m_img, idx));
}
void TileSheetEditorModel::insertTiles(
TileSheet::SubSheetIdx const&idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept {
pushCommand(ox::make<InsertTilesCommand>(m_img, idx, tileIdx, tileCnt));
std::ignore = pushCommand(ox::make<InsertTilesCommand>(m_img, idx, tileIdx, tileCnt));
}
void TileSheetEditorModel::deleteTiles(
TileSheet::SubSheetIdx const&idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept {
pushCommand(ox::make<DeleteTilesCommand>(m_img, idx, tileIdx, tileCnt));
std::ignore = pushCommand(ox::make<DeleteTilesCommand>(m_img, idx, tileIdx, tileCnt));
}
ox::Error TileSheetEditorModel::updateSubsheet(
TileSheet::SubSheetIdx const&idx, ox::StringViewCR name, int const cols, int const rows) noexcept {
OX_REQUIRE(cmd, ox::makeCatch<UpdateSubSheetCommand>(m_img, idx, name, cols, rows));
pushCommand(cmd);
std::ignore = pushCommand(cmd);
return {};
}
@@ -215,7 +208,7 @@ void TileSheetEditorModel::fill(ox::Point const&pt, int const palIdx) noexcept {
if (m_ongoingDrawCommand) {
m_updated = m_updated || m_ongoingDrawCommand->append(idxList);
} else if (getPixel(activeSubSheet, pt) != palIdx) {
pushCommand(ox::make<DrawCommand>(m_img, m_activeSubsSheetIdx, idxList, palIdx));
std::ignore = pushCommand(ox::make<DrawCommand>(m_img, m_activeSubsSheetIdx, idxList, palIdx));
}
}
@@ -288,6 +281,28 @@ bool TileSheetEditorModel::pixelSelected(std::size_t const idx) const noexcept {
return m_selection && m_selection->contains(pt);
}
ox::Error TileSheetEditorModel::flipX() noexcept {
auto const &ss = activeSubSheet();
ox::Point a;
ox::Point b{ss.columns * TileWidth - 1, ss.rows * TileHeight - 1};
if (m_selection) {
a = m_selection->a;
b = m_selection->b;
}
return pushCommand(ox::make<FlipXCommand>(m_img, m_activeSubsSheetIdx, a, b));
}
ox::Error TileSheetEditorModel::flipY() noexcept {
auto const &ss = activeSubSheet();
ox::Point a;
ox::Point b{ss.columns * TileWidth - 1, ss.rows * TileHeight - 1};
if (m_selection) {
a = m_selection->a;
b = m_selection->b;
}
return pushCommand(ox::make<FlipYCommand>(m_img, m_activeSubsSheetIdx, a, b));
}
void TileSheetEditorModel::getFillPixels(
TileSheet::SubSheet const&activeSubSheet,
ox::Span<bool> pixels,
@@ -318,10 +333,34 @@ void TileSheetEditorModel::getFillPixels(
}
}
void TileSheetEditorModel::pushCommand(studio::UndoCommand *cmd) noexcept {
void TileSheetEditorModel::setPalPath() noexcept {
auto &path = m_img.defaultPalette;
constexpr ox::StringView uuidPrefix = "uuid://";
if (ox::beginsWith(path, uuidPrefix)) {
auto const uuid = substr(path, uuidPrefix.bytes());
auto const out = keelCtx(m_tctx).uuidToPath.at(uuid);
if (!out.error) {
m_palPath = *out.value;
}
} else {
m_palPath = path;
}
}
ox::Error TileSheetEditorModel::pushCommand(studio::UndoCommand *cmd) noexcept {
std::ignore = m_undoStack.push(ox::UPtr<studio::UndoCommand>{cmd});
m_ongoingDrawCommand = dynamic_cast<DrawCommand*>(cmd);
m_updated = true;
return {};
}
ox::Error TileSheetEditorModel::handleFileRename(ox::StringViewCR, ox::StringViewCR newPath, ox::UUID const&id) noexcept {
if ((beginsWith(m_img.defaultPalette, "uuid://") &&
substr(m_img.defaultPalette, 7) == id.toString()) ||
m_img.defaultPalette == newPath) {
m_palPath = newPath;
}
return {};
}
}

View File

@@ -25,6 +25,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
studio::StudioContext &m_sctx;
turbine::Context &m_tctx;
ox::String m_path;
ox::String m_palPath;
TileSheet m_img;
TileSheet::SubSheetIdx m_activeSubsSheetIdx;
keel::AssetRef<Palette> m_pal;
@@ -36,7 +37,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
bool m_updated = false;
public:
TileSheetEditorModel(studio::StudioContext &sctx, ox::StringViewCR path, studio::UndoStack &undoStack);
TileSheetEditorModel(studio::StudioContext &sctx, ox::StringParam path, studio::UndoStack &undoStack);
~TileSheetEditorModel() override = default;
@@ -125,6 +126,10 @@ class TileSheetEditorModel: public ox::SignalHandler {
bool pixelSelected(std::size_t idx) const noexcept;
ox::Error flipX() noexcept;
ox::Error flipY() noexcept;
private:
void getFillPixels(
TileSheet::SubSheet const&activeSubSheet,
@@ -132,7 +137,11 @@ class TileSheetEditorModel: public ox::SignalHandler {
ox::Point const&pt,
int oldColor) const noexcept;
void pushCommand(studio::UndoCommand *cmd) noexcept;
void setPalPath() noexcept;
ox::Error pushCommand(studio::UndoCommand *cmd) noexcept;
ox::Error handleFileRename(ox::StringViewCR, ox::StringViewCR newPath, ox::UUID const&id) noexcept;
};

View File

@@ -103,6 +103,30 @@ ox::Error setPixelCount(TileSheet::SubSheet &ss, std::size_t const cnt) noexcept
return setPixelCount(ss.pixels, cnt);
}
void flipX(TileSheet::SubSheet &ss, ox::Point const &a, ox::Point const &b) noexcept {
auto const w = (b.x - a.x) / 2 + 1;
auto const pixCols = ss.columns * TileWidth;
for (int32_t yi = a.y; yi <= b.y; ++yi) {
for (int32_t xi = 0; xi < w; ++xi) {
auto const aIdx = ptToIdx(a.x + xi, yi, pixCols);
auto const bIdx = ptToIdx(b.x - xi, yi, pixCols);
std::swap(ss.pixels[aIdx], ss.pixels[bIdx]);
}
}
}
void flipY(TileSheet::SubSheet &ss, ox::Point const &a, ox::Point const &b) noexcept {
auto const h = (b.y - a.y) / 2 + 1;
auto const pixCols = ss.columns * TileWidth;
for (int32_t yi = 0; yi < h; ++yi) {
for (int32_t xi = a.x; xi <= b.x; ++xi) {
auto const aIdx = ptToIdx(xi, a.y + yi, pixCols);
auto const bIdx = ptToIdx(xi, b.y - yi, pixCols);
std::swap(ss.pixels[aIdx], ss.pixels[bIdx]);
}
}
}
unsigned pixelCnt(TileSheet::SubSheet const&ss) noexcept {
return static_cast<unsigned>(ss.pixels.size());
}