[nostalgia/core/studio] Add Fill tool to TileSheetEditor
This commit is contained in:
parent
b07cbe89a1
commit
a199cf113a
@ -29,6 +29,16 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
contextMenu.dismiss();
|
contextMenu.dismiss();
|
||||||
|
switch (sheetData.activeTool) {
|
||||||
|
case 'Fill':
|
||||||
|
{
|
||||||
|
var pixel = pixelAt(mouseX, mouseY);
|
||||||
|
if (pixel) {
|
||||||
|
sheetData.fillPixel(pixel);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,6 +204,49 @@ class ModPixelsCommand: public QUndoCommand {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FillPixelsCommand: public QUndoCommand {
|
||||||
|
private:
|
||||||
|
const std::array<bool, PixelsPerTile> m_pixelUpdateMap;
|
||||||
|
const std::size_t m_idx = 0;
|
||||||
|
const int m_oldColor = 0;
|
||||||
|
const int m_newColor = 0;
|
||||||
|
SheetData *const m_sheetData = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FillPixelsCommand(SheetData *sheetData, const std::array<bool, PixelsPerTile> &pum, int idx, int oldColor, int newColor):
|
||||||
|
m_pixelUpdateMap(pum),
|
||||||
|
m_idx(static_cast<std::size_t>(idx)),
|
||||||
|
m_oldColor(oldColor),
|
||||||
|
m_newColor(newColor),
|
||||||
|
m_sheetData(sheetData) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~FillPixelsCommand() = default;
|
||||||
|
|
||||||
|
int id() const override {
|
||||||
|
return static_cast<int>(CommandId::ModPixel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void redo() override {
|
||||||
|
for (std::size_t i = 0; i < m_pixelUpdateMap.size(); ++i) {
|
||||||
|
if (m_pixelUpdateMap[i]) {
|
||||||
|
m_sheetData->setPixel(i + m_idx, m_newColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_sheetData->notifyUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void undo() override {
|
||||||
|
for (std::size_t i = 0; i < m_pixelUpdateMap.size(); ++i) {
|
||||||
|
if (m_pixelUpdateMap[i]) {
|
||||||
|
m_sheetData->setPixel(i + m_idx, m_oldColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_sheetData->notifyUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class UpdatePixelsCommand: public QUndoCommand {
|
class UpdatePixelsCommand: public QUndoCommand {
|
||||||
private:
|
private:
|
||||||
struct PixelUpdate {
|
struct PixelUpdate {
|
||||||
@ -402,6 +445,18 @@ void TileSheetEditorColorTableDelegate::paint(QPainter *painter, const QStyleOpt
|
|||||||
SheetData::SheetData(QUndoStack *undoStack): m_cmdStack(undoStack) {
|
SheetData::SheetData(QUndoStack *undoStack): m_cmdStack(undoStack) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SheetData::fillPixel(QVariant pixelItem) {
|
||||||
|
const auto p = qobject_cast<QQuickItem*>(pixelItem.value<QObject*>());
|
||||||
|
if (p && p != m_prevPixelOperand) {
|
||||||
|
const auto idx = p->property("pixelNumber").toInt();
|
||||||
|
const auto pt = idxToPt(idx, m_columns);
|
||||||
|
const auto oldColor = m_pixels[idx];
|
||||||
|
std::array<bool, PixelsPerTile> pixels = {};
|
||||||
|
getFillPixels(pixels.data(), pt, oldColor);
|
||||||
|
m_cmdStack->push(new FillPixelsCommand(this, pixels, idx / PixelsPerTile * PixelsPerTile, oldColor, m_selectedColor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SheetData::selectPixel(QVariant pixelItem) {
|
void SheetData::selectPixel(QVariant pixelItem) {
|
||||||
auto p = qobject_cast<QQuickItem*>(pixelItem.value<QObject*>());
|
auto p = qobject_cast<QQuickItem*>(pixelItem.value<QObject*>());
|
||||||
if (p && p != m_prevPixelOperand) {
|
if (p && p != m_prevPixelOperand) {
|
||||||
@ -467,6 +522,38 @@ QString SheetData::palettePath() const {
|
|||||||
return m_currentPalettePath;
|
return m_currentPalettePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SheetData::getFillPixels(bool *pixels, common::Point pt, int oldColor) const {
|
||||||
|
const auto tileIdx = [this](const common::Point &pt) noexcept {
|
||||||
|
return ptToIdx(pt, m_columns) / PixelsPerTile;
|
||||||
|
};
|
||||||
|
// get points
|
||||||
|
const auto leftPt = pt + common::Point(-1, 0);
|
||||||
|
const auto rightPt = pt + common::Point(1, 0);
|
||||||
|
const auto topPt = pt + common::Point(0, -1);
|
||||||
|
const auto bottomPt = pt + common::Point(0, 1);
|
||||||
|
// calculate indices
|
||||||
|
const auto idx = ptToIdx(pt, m_columns);
|
||||||
|
const auto leftIdx = ptToIdx(leftPt, m_columns);
|
||||||
|
const auto rightIdx = ptToIdx(rightPt, m_columns);
|
||||||
|
const auto topIdx = ptToIdx(topPt, m_columns);
|
||||||
|
const auto bottomIdx = ptToIdx(bottomPt, m_columns);
|
||||||
|
const auto tile = tileIdx(pt);
|
||||||
|
// mark pixels to update
|
||||||
|
pixels[idx % PixelsPerTile] = true;
|
||||||
|
if (!pixels[leftIdx % PixelsPerTile] && tile == tileIdx(leftPt) && m_pixels[leftIdx] == oldColor) {
|
||||||
|
getFillPixels(pixels, leftPt, oldColor);
|
||||||
|
}
|
||||||
|
if (!pixels[rightIdx % PixelsPerTile] && tile == tileIdx(rightPt) && m_pixels[rightIdx] == oldColor) {
|
||||||
|
getFillPixels(pixels, rightPt, oldColor);
|
||||||
|
}
|
||||||
|
if (!pixels[topIdx % PixelsPerTile] && tile == tileIdx(topPt) && m_pixels[topIdx] == oldColor) {
|
||||||
|
getFillPixels(pixels, topPt, oldColor);
|
||||||
|
}
|
||||||
|
if (!pixels[bottomIdx % PixelsPerTile] && tile == tileIdx(bottomPt) && m_pixels[bottomIdx] == oldColor) {
|
||||||
|
getFillPixels(pixels, bottomPt, oldColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SheetData::load(const studio::Context *ctx, QString ngPath, QString palPath) {
|
void SheetData::load(const studio::Context *ctx, QString ngPath, QString palPath) {
|
||||||
auto ng = ctx->project->loadObj<NostalgiaGraphic>(ngPath);
|
auto ng = ctx->project->loadObj<NostalgiaGraphic>(ngPath);
|
||||||
if (palPath == "" && ng->defaultPalette.type() == ox::FileAddressType::Path) {
|
if (palPath == "" && ng->defaultPalette.type() == ox::FileAddressType::Path) {
|
||||||
@ -523,8 +610,7 @@ void SheetData::insertTile(int tileIdx, QVector<int> tileData) {
|
|||||||
m_pixels.insert(pxIdx, PixelsPerTile, 0);
|
m_pixels.insert(pxIdx, PixelsPerTile, 0);
|
||||||
m_pixelSelected.insert(pxIdx, PixelsPerTile, 0);
|
m_pixelSelected.insert(pxIdx, PixelsPerTile, 0);
|
||||||
std::copy(tileData.begin(), tileData.end(), &m_pixels[pxIdx]);
|
std::copy(tileData.begin(), tileData.end(), &m_pixels[pxIdx]);
|
||||||
emit pixelsChanged();
|
notifyUpdate();
|
||||||
emit changeOccurred();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<int> SheetData::deleteTile(int tileIdx) {
|
QVector<int> SheetData::deleteTile(int tileIdx) {
|
||||||
@ -535,8 +621,7 @@ QVector<int> SheetData::deleteTile(int tileIdx) {
|
|||||||
std::back_inserter(out));
|
std::back_inserter(out));
|
||||||
m_pixels.remove(pxIdx, PixelsPerTile);
|
m_pixels.remove(pxIdx, PixelsPerTile);
|
||||||
m_pixelSelected.remove(pxIdx, PixelsPerTile);
|
m_pixelSelected.remove(pxIdx, PixelsPerTile);
|
||||||
emit pixelsChanged();
|
notifyUpdate();
|
||||||
emit changeOccurred();
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,6 +646,14 @@ void SheetData::modPixels(const QHash<int, int> &mods, int inversion) {
|
|||||||
const auto mod = mods[index];
|
const auto mod = mods[index];
|
||||||
m_pixels[index] += mod * inversion;
|
m_pixels[index] += mod * inversion;
|
||||||
}
|
}
|
||||||
|
notifyUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SheetData::setPixel(int index, int color) {
|
||||||
|
m_pixels[index] = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SheetData::notifyUpdate() {
|
||||||
emit pixelsChanged();
|
emit pixelsChanged();
|
||||||
emit changeOccurred();
|
emit changeOccurred();
|
||||||
}
|
}
|
||||||
@ -654,8 +747,7 @@ void SheetData::pasteClipboard() {
|
|||||||
|
|
||||||
void SheetData::applyClipboard(const TileSheetClipboard &cb) {
|
void SheetData::applyClipboard(const TileSheetClipboard &cb) {
|
||||||
cb.pastePixels(cb.point1(), &m_pixels, m_columns);
|
cb.pastePixels(cb.point1(), &m_pixels, m_columns);
|
||||||
emit pixelsChanged();
|
notifyUpdate();
|
||||||
emit changeOccurred();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SheetData::markSelection(common::Point selectionEnd) {
|
void SheetData::markSelection(common::Point selectionEnd) {
|
||||||
@ -739,7 +831,7 @@ TileSheetEditor::TileSheetEditor(QString path, const studio::Context *ctx, QWidg
|
|||||||
auto updateAfterBtn = new QPushButton(tr("&Mod GTE"), tb);
|
auto updateAfterBtn = new QPushButton(tr("&Mod GTE"), tb);
|
||||||
m_toolBtns.addButton(new QPushButton(tr("&Select"), tb), static_cast<int>(TileSheetTool::Select));
|
m_toolBtns.addButton(new QPushButton(tr("&Select"), tb), static_cast<int>(TileSheetTool::Select));
|
||||||
m_toolBtns.addButton(new QPushButton(tr("&Draw"), tb), static_cast<int>(TileSheetTool::Draw));
|
m_toolBtns.addButton(new QPushButton(tr("&Draw"), tb), static_cast<int>(TileSheetTool::Draw));
|
||||||
//m_toolBtns.addButton(new QPushButton(tr("F&ill"), tb), static_cast<int>(TileSheetTool::Fill));
|
m_toolBtns.addButton(new QPushButton(tr("F&ill"), tb), static_cast<int>(TileSheetTool::Fill));
|
||||||
tb->addWidget(m_tilesX);
|
tb->addWidget(m_tilesX);
|
||||||
tb->addWidget(m_tilesY);
|
tb->addWidget(m_tilesY);
|
||||||
tb->addSeparator();
|
tb->addSeparator();
|
||||||
|
@ -135,7 +135,9 @@ class SheetData: public QObject {
|
|||||||
TileSheetClipboard m_clipboard;
|
TileSheetClipboard m_clipboard;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SheetData(QUndoStack*);
|
explicit SheetData(QUndoStack*);
|
||||||
|
|
||||||
|
Q_INVOKABLE void fillPixel(QVariant pixel);
|
||||||
|
|
||||||
Q_INVOKABLE void selectPixel(QVariant pixel);
|
Q_INVOKABLE void selectPixel(QVariant pixel);
|
||||||
|
|
||||||
@ -161,6 +163,8 @@ class SheetData: public QObject {
|
|||||||
|
|
||||||
[[nodiscard]] QString palettePath() const;
|
[[nodiscard]] QString palettePath() const;
|
||||||
|
|
||||||
|
void getFillPixels(bool *pixels, common::Point pt, int oldColor) const;
|
||||||
|
|
||||||
void load(const studio::Context *ctx, QString ngPath, QString palPath = "");
|
void load(const studio::Context *ctx, QString ngPath, QString palPath = "");
|
||||||
|
|
||||||
void reload(const studio::Context *ctx);
|
void reload(const studio::Context *ctx);
|
||||||
@ -181,6 +185,10 @@ class SheetData: public QObject {
|
|||||||
|
|
||||||
void modPixels(const QHash<int, int> &pixels, int inversion);
|
void modPixels(const QHash<int, int> &pixels, int inversion);
|
||||||
|
|
||||||
|
void setPixel(int index, int color);
|
||||||
|
|
||||||
|
void notifyUpdate();
|
||||||
|
|
||||||
[[nodiscard]] std::unique_ptr<NostalgiaGraphic> toNostalgiaGraphic() const;
|
[[nodiscard]] std::unique_ptr<NostalgiaGraphic> toNostalgiaGraphic() const;
|
||||||
|
|
||||||
[[nodiscard]] int activeTool() const;
|
[[nodiscard]] int activeTool() const;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user