[nostalgia/core] Add Fill command to tilesheet editor and make ClipboardObject more efficient

This commit is contained in:
2022-03-11 20:41:36 -06:00
parent ae80d22769
commit 7df978605f
7 changed files with 391 additions and 337 deletions
@@ -15,301 +15,6 @@
namespace nostalgia::core {
class TileSheetClipboard {
public:
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.TileSheetClipboard";
static constexpr auto TypeVersion = 1;
oxModelFriend(TileSheetClipboard);
struct Pixel {
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.TileSheetClipboard.Pixel";
static constexpr auto TypeVersion = 1;
uint16_t colorIdx = 0;
geo::Point pt;
};
protected:
ox::Vector<Pixel> m_pixels;
public:
void addPixel(const geo::Point &pt, uint16_t colorIdx) noexcept;
[[nodiscard]]
bool empty() const;
void pastePixels(const geo::Point &pt, ox::Vector<int> *tgt, int tgtColumns) const;
void clear() noexcept;
constexpr const ox::Vector<Pixel> &pixels() const noexcept {
return m_pixels;
}
};
oxModelBegin(TileSheetClipboard::Pixel)
oxModelField(colorIdx)
oxModelField(pt)
oxModelEnd()
oxModelBegin(TileSheetClipboard)
oxModelFieldRename(pixels, m_pixels)
oxModelEnd()
// Command IDs to use with QUndoCommand::id()
enum class CommandId {
Draw = 1,
AddSubSheet = 2,
RmSubSheet = 3,
UpdateSubSheet = 4,
Paste = 5,
};
constexpr bool operator==(CommandId c, int i) noexcept {
return static_cast<int>(c) == i;
}
constexpr bool operator==(int i, CommandId c) noexcept {
return static_cast<int>(c) == i;
}
struct DrawCommand: public studio::UndoCommand {
private:
struct Change {
uint32_t idx = 0;
uint16_t oldPalIdx = 0;
};
TileSheet *m_img = nullptr;
TileSheet::SubSheetIdx m_subSheetIdx;
ox::Vector<Change, 8> m_changes;
int m_palIdx = 0;
public:
constexpr DrawCommand(TileSheet *img, const TileSheet::SubSheetIdx &subSheetIdx, std::size_t idx, int palIdx) noexcept {
m_img = img;
auto &subsheet = m_img->getSubSheet(subSheetIdx);
m_subSheetIdx = subSheetIdx;
m_changes.emplace_back(idx, subsheet.getPixel(m_img->bpp, idx));
m_palIdx = palIdx;
}
constexpr auto append(std::size_t idx) noexcept {
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) {
return c.idx == idx;
});
if (existing == m_changes.cend()) {
m_changes.emplace_back(idx, subsheet.getPixel(m_img->bpp, idx));
subsheet.setPixel(m_img->bpp, idx, m_palIdx);
return true;
}
}
return false;
}
void redo() noexcept final {
auto &subsheet = m_img->getSubSheet(m_subSheetIdx);
for (const auto &c : m_changes) {
subsheet.setPixel(m_img->bpp, c.idx, m_palIdx);
}
}
void undo() noexcept final {
auto &subsheet = m_img->getSubSheet(m_subSheetIdx);
for (const auto &c : m_changes) {
subsheet.setPixel(m_img->bpp, c.idx, c.oldPalIdx);
}
}
[[nodiscard]]
int commandId() const noexcept final {
return static_cast<int>(CommandId::Draw);
}
};
struct PasteCommand: public studio::UndoCommand {
private:
struct Change {
uint32_t idx = 0;
uint16_t newPalIdx = 0;
uint16_t oldPalIdx = 0;
};
TileSheet *m_img = nullptr;
TileSheet::SubSheetIdx m_subSheetIdx;
ox::Vector<Change> m_changes;
public:
constexpr PasteCommand(TileSheet *img, const TileSheet::SubSheetIdx &subSheetIdx, const geo::Point &dstStart, const geo::Point &dstEnd, const TileSheetClipboard &cb) noexcept {
m_img = img;
m_subSheetIdx = subSheetIdx;
const auto &subsheet = m_img->getSubSheet(subSheetIdx);
for (const auto &p : cb.pixels()) {
const auto dstPt = p.pt + dstStart;
if (dstPt.x <= dstEnd.x && dstPt.y <= dstEnd.y) {
const auto idx = subsheet.idx(dstPt);
m_changes.emplace_back(idx, p.colorIdx, subsheet.getPixel(m_img->bpp, idx));
}
}
}
void redo() noexcept final {
auto &subsheet = m_img->getSubSheet(m_subSheetIdx);
for (const auto &c : m_changes) {
subsheet.setPixel(m_img->bpp, c.idx, c.newPalIdx);
}
}
void undo() noexcept final {
auto &subsheet = m_img->getSubSheet(m_subSheetIdx);
for (const auto &c : m_changes) {
subsheet.setPixel(m_img->bpp, c.idx, c.oldPalIdx);
}
}
[[nodiscard]]
int commandId() const noexcept final {
return static_cast<int>(CommandId::Paste);
}
};
struct AddSubSheetCommand: public studio::UndoCommand {
private:
TileSheet *m_img = nullptr;
TileSheet::SubSheetIdx m_parentIdx;
ox::Vector<TileSheet::SubSheetIdx, 2> m_addedSheets;
public:
constexpr AddSubSheetCommand(TileSheet *img, const TileSheet::SubSheetIdx &parentIdx) noexcept {
m_img = img;
m_parentIdx = parentIdx;
auto &parent = m_img->getSubSheet(m_parentIdx);
if (parent.subsheets.size()) {
auto idx = m_parentIdx;
idx.emplace_back(parent.subsheets.size());
m_addedSheets.push_back(idx);
} else {
auto idx = m_parentIdx;
idx.emplace_back(0);
m_addedSheets.push_back(idx);
idx.back().value = 1;
m_addedSheets.push_back(idx);
}
}
void redo() noexcept final {
auto &parent = m_img->getSubSheet(m_parentIdx);
if (m_addedSheets.size() < 2) {
auto i = parent.subsheets.size();
parent.subsheets.emplace_back(ox::sfmt("Subsheet {}", i).c_str(), 1, 1);
} else {
parent.subsheets.emplace_back("Subsheet 0", parent.columns, parent.rows, std::move(parent.pixels));
parent.rows = 0;
parent.columns = 0;
parent.subsheets.emplace_back("Subsheet 1", 1, 1);
}
}
void undo() noexcept final {
auto &parent = m_img->getSubSheet(m_parentIdx);
if (parent.subsheets.size() == 2) {
auto s = parent.subsheets[0];
parent.rows = s.rows;
parent.columns = s.columns;
parent.pixels = std::move(s.pixels);
parent.subsheets.clear();
} else {
for (auto idx = m_addedSheets.rbegin(); idx != m_addedSheets.rend(); ++idx) {
oxLogError(m_img->rmSubSheet(*idx));
}
}
}
[[nodiscard]]
int commandId() const noexcept final {
return static_cast<int>(CommandId::AddSubSheet);
}
};
struct RmSubSheetCommand: public studio::UndoCommand {
private:
TileSheet *m_img = nullptr;
TileSheet::SubSheetIdx m_idx;
TileSheet::SubSheetIdx m_parentIdx;
TileSheet::SubSheet m_sheet;
public:
constexpr RmSubSheetCommand(TileSheet *img, const TileSheet::SubSheetIdx &idx) noexcept {
m_img = img;
m_idx = idx;
m_parentIdx = idx;
m_parentIdx.pop_back();
auto &parent = m_img->getSubSheet(m_parentIdx);
m_sheet = parent.subsheets[idx.back().value];
}
void redo() noexcept final {
auto &parent = m_img->getSubSheet(m_parentIdx);
oxLogError(parent.subsheets.erase(m_idx.back().value).error);
}
void undo() noexcept final {
auto &parent = m_img->getSubSheet(m_parentIdx);
auto i = m_idx.back().value;
parent.subsheets.insert(i, m_sheet);
}
[[nodiscard]]
int commandId() const noexcept final {
return static_cast<int>(CommandId::RmSubSheet);
}
};
struct UpdateSubSheetCommand: public studio::UndoCommand {
private:
TileSheet *m_img = nullptr;
TileSheet::SubSheetIdx m_idx;
TileSheet::SubSheet m_sheet;
ox::String m_newName;
int m_newCols = 0;
int m_newRows = 0;
public:
constexpr UpdateSubSheetCommand(TileSheet *img, const TileSheet::SubSheetIdx &idx,
const ox::String &name, int cols, int rows) noexcept {
m_img = img;
m_idx = idx;
m_sheet = img->getSubSheet(idx);
m_newName = name;
m_newCols = cols;
m_newRows = rows;
}
void redo() noexcept final {
auto &sheet = m_img->getSubSheet(m_idx);
sheet.name = m_newName;
sheet.columns = m_newCols;
sheet.rows = m_newRows;
oxLogError(sheet.setPixelCount(m_img->bpp, static_cast<std::size_t>(PixelsPerTile * m_newCols * m_newRows)));
}
void undo() noexcept final {
auto &sheet = m_img->getSubSheet(m_idx);
sheet = m_sheet;
}
[[nodiscard]]
int commandId() const noexcept final {
return static_cast<int>(CommandId::UpdateSubSheet);
}
};
class TileSheetEditorModel: public ox::SignalHandler {
public:
@@ -320,7 +25,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
TileSheet::SubSheetIdx m_activeSubsSheetIdx;
AssetRef<Palette> m_pal;
studio::UndoStack m_undoStack;
DrawCommand *m_ongoingDrawCommand = nullptr;
class DrawCommand *m_ongoingDrawCommand = nullptr;
bool m_updated = false;
Context *m_ctx = nullptr;
ox::String m_path;
@@ -374,6 +79,8 @@ class TileSheetEditorModel: public ox::SignalHandler {
return m_activeSubsSheetIdx;
}
void fill(const geo::Point &pt, int palIdx) noexcept;
void select(const geo::Point &pt) noexcept;
void completeSelection() noexcept;
@@ -394,8 +101,6 @@ class TileSheetEditorModel: public ox::SignalHandler {
bool pixelSelected(std::size_t idx) const noexcept;
protected:
void saveItem();
void getFillPixels(bool *pixels, const geo::Point &pt, int oldColor) const noexcept;
private: