Files
ox/src/nostalgia/modules/core/src/tilesheet.cpp
T

381 lines
10 KiB
C++

/*
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <ox/std/size.hpp>
#include <ox/std/vector.hpp>
#include <nostalgia/core/ptidxconv.hpp>
#include <nostalgia/core/tilesheet.hpp>
namespace nostalgia::core {
TileSheet::SubSheet::SubSheet(SubSheet &&other) noexcept:
id (other.id),
name (std::move(other.name)),
columns (other.columns),
rows (other.rows),
subsheets(std::move(other.subsheets)),
pixels (std::move(other.pixels)) {
other.name = "";
other.columns = {};
other.rows = {};
}
TileSheet::SubSheet::SubSheet(
SubSheetId pId,
ox::CRStringView pName,
int pColumns,
int pRows,
int bpp) noexcept:
id(pId),
name(pName),
columns(pColumns),
rows(pRows),
pixels(static_cast<std::size_t>(columns * rows * PixelsPerTile) / (bpp == 4 ? 2u : 1u)) {
}
TileSheet::SubSheet::SubSheet(
SubSheetId pId,
ox::CRStringView pName,
int pColumns,
int pRows,
ox::Vector<uint8_t> pPixels) noexcept:
id(pId),
name(pName),
columns(pColumns),
rows(pRows),
pixels(std::move(pPixels)) {
}
TileSheet::SubSheet &TileSheet::SubSheet::operator=(TileSheet::SubSheet &&other) noexcept {
name = std::move(other.name);
columns = other.columns;
rows = other.rows;
subsheets = std::move(other.subsheets);
pixels = std::move(other.pixels);
return *this;
}
std::size_t TileSheet::SubSheet::idx(const ox::Point &pt) const noexcept {
return ptToIdx(pt, columns);
}
void TileSheet::SubSheet::readPixelsTo(ox::Vector<uint8_t> *pPixels, int8_t pBpp) const noexcept {
if (!subsheets.empty()) {
for (auto &s: subsheets) {
s.readPixelsTo(pPixels);
}
} else {
if (pBpp == 4) {
for (auto p: this->pixels) {
pPixels->emplace_back(static_cast<uint8_t>(p & 0b1111));
pPixels->emplace_back(static_cast<uint8_t>(p >> 4));
}
} else {
for (auto p: this->pixels) {
pPixels->emplace_back(p);
}
}
}
}
void TileSheet::SubSheet::readPixelsTo(ox::Vector<uint8_t> *pPixels) const noexcept {
if (!subsheets.empty()) {
for (auto &s: subsheets) {
s.readPixelsTo(pPixels);
}
} else {
for (auto p : this->pixels) {
pPixels->emplace_back(p);
}
}
}
std::size_t TileSheet::SubSheet::unusedPixels() const noexcept {
std::size_t childrenSize = 0;
for (auto &c : subsheets) {
childrenSize += c.size();
}
return size() - childrenSize;
}
uint8_t TileSheet::SubSheet::getPixel4Bpp(std::size_t idx) const noexcept {
if (idx & 1) {
return this->pixels[idx / 2] >> 4;
} else {
return this->pixels[idx / 2] & 0b0000'1111;
}
}
uint8_t TileSheet::SubSheet::getPixel8Bpp(std::size_t idx) const noexcept {
return this->pixels[idx];
}
uint8_t TileSheet::SubSheet::getPixel(int8_t pBpp, std::size_t idx) const noexcept {
if (pBpp == 4) {
return getPixel4Bpp(idx);
} else {
return getPixel8Bpp(idx);
}
}
uint8_t TileSheet::SubSheet::getPixel4Bpp(const ox::Point &pt) const noexcept {
const auto idx = ptToIdx(pt, columns);
return getPixel4Bpp(idx);
}
uint8_t TileSheet::SubSheet::getPixel8Bpp(const ox::Point &pt) const noexcept {
const auto idx = ptToIdx(pt, columns);
return getPixel8Bpp(idx);
}
uint8_t TileSheet::SubSheet::getPixel(int8_t pBpp, const ox::Point &pt) const noexcept {
const auto idx = ptToIdx(pt, columns);
return getPixel(pBpp, idx);
}
void TileSheet::SubSheet::setPixel(int8_t pBpp, uint64_t idx, uint8_t palIdx) noexcept {
auto &pixel = this->pixels[static_cast<std::size_t>(idx / 2)];
if (pBpp == 4) {
if (idx & 1) {
pixel = static_cast<uint8_t>((pixel & 0b0000'1111) | (palIdx << 4));
} else {
pixel = (pixel & 0b1111'0000) | (palIdx);
}
} else {
pixel = palIdx;
}
}
void TileSheet::SubSheet::setPixel(int8_t pBpp, const ox::Point &pt, uint8_t palIdx) noexcept {
const auto idx = ptToIdx(pt, columns);
setPixel(pBpp, idx, palIdx);
}
ox::Error TileSheet::SubSheet::setPixelCount(int8_t pBpp, std::size_t cnt) noexcept {
switch (pBpp) {
case 4:
pixels.resize(cnt / 2);
return OxError(0);
case 8:
pixels.resize(cnt);
return OxError(0);
default:
return OxError(1, "Invalid pBpp used for TileSheet::SubSheet::setPixelCount");
}
}
unsigned TileSheet::SubSheet::pixelCnt(int8_t pBpp) const noexcept {
const auto pixelsSize = static_cast<unsigned>(pixels.size());
return pBpp == 4 ? pixelsSize * 2 : pixelsSize;
}
ox::Result<unsigned> TileSheet::SubSheet::getTileOffset(
ox::SpanView<ox::StringView> const&pNamePath,
int8_t pBpp,
std::size_t pIt,
unsigned pCurrentTotal) const noexcept {
// pIt == pNamePath.size() - 1 &&
if (name != pNamePath[pIt]) {
return OxError(2, "Wrong branch");
}
if (pIt == pNamePath.size() - 1) {
return pCurrentTotal;
}
for (auto &sub : subsheets) {
auto [offset, err] = sub.getTileOffset(
pNamePath, pBpp, pIt + 1, pCurrentTotal);
if (!err) {
return offset;
}
pCurrentTotal += sub.pixelCnt(pBpp) / PixelsPerTile;
}
return OxError(1, "SubSheet not found");
}
ox::Result<SubSheetId> TileSheet::SubSheet::getIdFor(
ox::SpanView<ox::StringView> const&pNamePath,
std::size_t pIt) const noexcept {
for (auto &sub : subsheets) {
if (sub.name == pNamePath[pIt]) {
if (pIt == pNamePath.size()) {
return id;
}
return getIdFor(pNamePath, pIt + 1);
}
}
return OxError(1, "SubSheet not found");
}
ox::Result<ox::StringView> TileSheet::SubSheet::getNameFor(SubSheetId pId) const noexcept {
if (id == pId) {
return ox::StringView(name);
}
for (const auto &sub : subsheets) {
const auto [name, err] = sub.getNameFor(pId);
if (!err) {
return name;
}
}
return OxError(1, "SubSheet not found");
}
TileSheet &TileSheet::operator=(const TileSheet &other) noexcept {
if (this != &other) {
bpp = other.bpp;
idIt = other.idIt;
defaultPalette = other.defaultPalette;
subsheet = other.subsheet;
}
return *this;
}
TileSheet &TileSheet::operator=(TileSheet &&other) noexcept {
bpp = other.bpp;
idIt = other.idIt;
defaultPalette = std::move(other.defaultPalette);
subsheet = std::move(other.subsheet);
return *this;
}
TileSheet::SubSheetIdx TileSheet::validateSubSheetIdx(
const SubSheetIdx &pIdx,
std::size_t pIdxIt,
const SubSheet *pSubsheet) noexcept {
if (pIdxIt == pIdx.size()) {
return pIdx;
}
const auto currentIdx = pIdx[pIdxIt];
if (pSubsheet->subsheets.size() <= currentIdx) {
auto out = pIdx;
if (!pSubsheet->subsheets.empty()) {
*out.back().value = pSubsheet->subsheets.size() - 1;
} else {
out.pop_back();
}
return out;
}
return validateSubSheetIdx(pIdx, pIdxIt + 1, &pSubsheet->subsheets[pIdx[pIdxIt]]);
}
TileSheet::SubSheetIdx TileSheet::validateSubSheetIdx(const SubSheetIdx &idx) noexcept {
return validateSubSheetIdx(idx, 0, &subsheet);
}
const TileSheet::SubSheet &TileSheet::getSubSheet(
const TileSheet::SubSheetIdx &idx,
std::size_t idxIt,
const SubSheet *pSubsheet) noexcept {
if (idxIt == idx.size()) {
return *pSubsheet;
}
const auto currentIdx = idx[idxIt];
if (pSubsheet->subsheets.size() < currentIdx) {
return *pSubsheet;
}
return getSubSheet(idx, idxIt + 1, &pSubsheet->subsheets[currentIdx]);
}
TileSheet::SubSheet &TileSheet::getSubSheet(
const TileSheet::SubSheetIdx &idx,
std::size_t idxIt,
TileSheet::SubSheet *pSubsheet) noexcept {
if (idxIt == idx.size()) {
return *pSubsheet;
}
return getSubSheet(idx, idxIt + 1, &pSubsheet->subsheets[idx[idxIt]]);
}
const TileSheet::SubSheet &TileSheet::getSubSheet(const TileSheet::SubSheetIdx &idx) const noexcept {
return getSubSheet(idx, 0, &subsheet);
}
TileSheet::SubSheet &TileSheet::getSubSheet(const TileSheet::SubSheetIdx &idx) noexcept {
return getSubSheet(idx, 0, &subsheet);
}
ox::Error TileSheet::addSubSheet(const TileSheet::SubSheetIdx &idx) noexcept {
auto &parent = getSubSheet(idx);
if (parent.subsheets.size() < 2) {
parent.subsheets.emplace_back(idIt++, ox::sfmt("Subsheet {}", parent.subsheets.size()), 1, 1, bpp);
} else {
parent.subsheets.emplace_back(idIt++, "Subsheet 0", parent.columns, parent.rows, bpp);
parent.subsheets.emplace_back(idIt++, "Subsheet 1", 1, 1, bpp);
}
return OxError(0);
}
ox::Error TileSheet::rmSubSheet(
const SubSheetIdx &idx,
std::size_t idxIt,
SubSheet *pSubsheet) noexcept {
if (idxIt == idx.size() - 1) {
return pSubsheet->subsheets.erase(idx[idxIt]).error;
}
return rmSubSheet(idx, idxIt + 1, &pSubsheet->subsheets[idx[idxIt]]);
}
ox::Error TileSheet::rmSubSheet(const TileSheet::SubSheetIdx &idx) noexcept {
return rmSubSheet(idx, 0, &subsheet);
}
uint8_t TileSheet::getPixel4Bpp(
const ox::Point &pt,
const TileSheet::SubSheetIdx &subsheetIdx) const noexcept {
oxAssert(bpp == 4, "TileSheet::getPixel4Bpp: wrong bpp");
auto &s = this->getSubSheet(subsheetIdx);
const auto idx = ptToIdx(pt, s.columns);
return s.getPixel4Bpp(idx);
}
uint8_t TileSheet::getPixel8Bpp(
const ox::Point &pt,
const TileSheet::SubSheetIdx &subsheetIdx) const noexcept {
oxAssert(bpp == 8, "TileSheet::getPixel8Bpp: wrong bpp");
auto &s = this->getSubSheet(subsheetIdx);
const auto idx = ptToIdx(pt, s.columns);
return s.getPixel8Bpp(idx);
}
ox::Result<SubSheetId> TileSheet::getIdFor(ox::CRStringView path) const noexcept {
return subsheet.getIdFor(ox::split<8>(path, '.'));
}
ox::Result<unsigned> TileSheet::getTileOffset(ox::CRStringView pNamePath) const noexcept {
return subsheet.getTileOffset(ox::split<8>(pNamePath, '.'), bpp);
}
ox::Result<ox::StringView> TileSheet::getNameFor(SubSheetId pId) const noexcept {
return subsheet.getNameFor(pId);
}
ox::Vector<uint8_t> TileSheet::pixels() const noexcept {
ox::Vector<uint8_t> out;
subsheet.readPixelsTo(&out);
return out;
}
ox::Vector<uint32_t> resizeTileSheetData(
ox::Vector<uint32_t> const&srcPixels,
ox::Size const&srcSize,
int scale) noexcept {
ox::Vector<uint32_t> dst;
auto dstWidth = srcSize.width * scale;
auto dstHeight = srcSize.height * scale;
const auto pixelCnt = dstWidth * dstHeight;
dst.resize(static_cast<std::size_t>(pixelCnt));
for (auto i = 0; i < pixelCnt; ++i) {
const auto dstPt = idxToPt(i, 1, scale);
const auto srcPt = dstPt / ox::Point{scale, scale};
const auto srcIdx = ptToIdx(srcPt, 1);
const auto srcPixel = srcPixels[srcIdx];
dst[static_cast<std::size_t>(i)] = srcPixel;
}
return dst;
}
}