Files
jasper/src/nostalgia/modules/gfx/src/tilesheet.cpp
Gary Talent 4e94c92568 Squashed 'deps/nostalgia/' changes from 2a8e3c2d..26fc5565
26fc5565 [nostalgia/gfx] Make dangling reference warning suppressions check for GCC 13
388541ce [nostalgia/player] Cleanup
6c194667 [nostalgia] Fix NostalgiaGfx lib name, stub out sound package
62d0579f [ox/fs] Restructure stat error handling to make easier to debug
202595b2 [keel] Fix loading assets by path
cb21ff3f Merge commit 'a6b9657268eb3fe139b0c22df27c2cb2efc0013c'
8459d3ba Merge commit 'c42adc290cd8a27d01bb6d9877032dd2c963a4b7'
8d04af69 Merge commit 'ab760b064fd6a302bad13274e0e02b2b2c957b67'
6c34198f Merge commit '897a59cdad66e593fd45eece9414d8414fa7f1ae'
f63c5816 [studio] Add filepickerpopup.hpp to studio.hpp

git-subtree-dir: deps/nostalgia
git-subtree-split: 26fc5565e86e09c6c51a615683fd9003816a24ac
2025-02-20 20:11:03 -06:00

429 lines
12 KiB
C++

/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <ox/std/size.hpp>
#include <ox/std/vector.hpp>
#include <nostalgia/gfx/ptidxconv.hpp>
#include <nostalgia/gfx/tilesheet.hpp>
namespace nostalgia::gfx {
std::size_t idx(TileSheet::SubSheet const&ss, ox::Point const&pt) noexcept {
return ptToIdx(pt, ss.columns);
}
[[nodiscard]]
static TileSheet::SubSheet const *getSubsheet(TileSheet::SubSheet const&ss, SubSheetId const id) noexcept {
if (ss.id == id) {
return &ss;
}
for (auto const&child: ss.subsheets) {
if (auto out = getSubsheet(child, id)) {
return out;
}
}
return {};
}
[[nodiscard]]
static size_t getTileCnt(TileSheet::SubSheet const&ss) noexcept {
if (ss.subsheets.empty()) {
return ss.pixels.size() / PixelsPerTile;
} else {
size_t out{};
for (auto const&child: ss.subsheets) {
out += getTileCnt(child);
}
return out;
}
}
size_t getTileCnt(TileSheet const&ts) noexcept {
return getTileCnt(ts.subsheet);
}
TileSheet::SubSheet const *getSubsheet(TileSheet const&ts, SubSheetId const id) noexcept {
return getSubsheet(ts.subsheet, id);
}
static ox::Optional<size_t> getPixelIdx(
TileSheet::SubSheet const&ss,
SubSheetId const id,
size_t &idx) noexcept {
for (auto const&child: ss.subsheets) {
if (child.id == id) {
return ox::Optional<size_t>(ox::in_place, idx);
}
if (auto out = getPixelIdx(child, id, idx)) {
return out;
}
idx += pixelCnt(child);
}
return ox::Optional<size_t>{};
}
ox::Optional<size_t> getTileIdx(TileSheet const&ts, SubSheetId const id) noexcept {
size_t idx{};
auto const out = getPixelIdx(ts.subsheet, id, idx);
return out ? ox::Optional<size_t>{ox::in_place, *out / PixelsPerTile} : out;
}
uint8_t getPixel(TileSheet::SubSheet const&ss, std::size_t const idx) noexcept {
return ss.pixels[idx];
}
uint8_t getPixel(TileSheet::SubSheet const&ss, ox::Point const&pt) noexcept {
auto const idx = ptToIdx(pt, ss.columns);
return getPixel(ss, idx);
}
static void setPixel(
ox::Vector<uint8_t> &pixels,
int const columns,
ox::Point const&pt,
uint8_t const palIdx) noexcept {
const auto idx = ptToIdx(pt, columns);
pixels[idx] = palIdx;
}
void setPixel(TileSheet::SubSheet &ss, ox::Point const&pt, uint8_t const palIdx) noexcept {
const auto idx = ptToIdx(pt, ss.columns);
ss.pixels[idx] = palIdx;
}
static ox::Error setPixelCount(ox::Vector<uint8_t> &pixels, std::size_t const cnt) noexcept {
pixels.reserve(cnt);
pixels.resize(cnt);
return {};
}
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());
}
ox::Error resizeSubsheet(TileSheet::SubSheet &ss, ox::Size const&sz) noexcept {
ox::Vector<uint8_t> out;
OX_RETURN_ERROR(setPixelCount(out, static_cast<size_t>(sz.width * sz.height) * PixelsPerTile));
auto const w = ox::min<int32_t>(ss.columns, sz.width) * TileWidth;
auto const h = ox::min<int32_t>(ss.rows, sz.height) * TileHeight;
for (auto x = 0; x < w; ++x) {
for (auto y = 0; y < h; ++y) {
auto const palIdx = getPixel(ss, {x, y});
setPixel(out, sz.width, {x, y}, palIdx);
}
}
ss.columns = sz.width;
ss.rows = sz.height;
ss.pixels = std::move(out);
return {};
}
ox::Result<ox::StringView> getNameFor(TileSheet::SubSheet const&ss, SubSheetId const pId) noexcept {
if (ss.id == pId) {
return ox::StringView(ss.name);
}
for (const auto &sub : ss.subsheets) {
const auto [name, err] = getNameFor(sub, pId);
if (!err) {
return name;
}
}
return ox::Error(1, "SubSheet not found");
}
TileSheet::SubSheetIdx validateSubSheetIdx(
TileSheet::SubSheetIdx &&pIdx,
std::size_t const pIdxIt,
TileSheet::SubSheet const&pSubsheet) noexcept {
if (pIdxIt >= pIdx.size()) {
return std::move(pIdx);
}
auto &currentIdx = pIdx[pIdxIt];
if (pSubsheet.subsheets.size() <= currentIdx) {
if (pSubsheet.subsheets.empty()) {
// currentIdx could not be repaired, remove
// this and all succeeding idxs and return
pIdx.resize(pIdxIt);
return std::move(pIdx);
} else {
currentIdx = static_cast<uint32_t>(pSubsheet.subsheets.size() - 1);
}
}
return validateSubSheetIdx(std::move(pIdx), pIdxIt + 1, pSubsheet.subsheets[currentIdx]);
}
TileSheet::SubSheetIdx validateSubSheetIdx(TileSheet const&ts, TileSheet::SubSheetIdx idx) noexcept {
return validateSubSheetIdx(std::move(idx), 0, ts.subsheet);
}
#if defined(__GNUC__) && __GNUC__ >= 13
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdangling-reference"
#endif
static TileSheet::SubSheet const&getSubSheet(
ox::SpanView<uint32_t> const &idx,
std::size_t const idxIt,
TileSheet::SubSheet const &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]);
}
#if defined(__GNUC__) && __GNUC__ >= 13
#pragma GCC diagnostic pop
#endif
TileSheet::SubSheet &getSubSheet(
ox::SpanView<uint32_t> const &idx,
std::size_t const idxIt,
TileSheet::SubSheet &pSubsheet) noexcept {
if (idxIt == idx.size()) {
return pSubsheet;
}
return getSubSheet(idx, idxIt + 1, pSubsheet.subsheets[idx[idxIt]]);
}
#if defined(__GNUC__) && __GNUC__ >= 13
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdangling-reference"
#endif
TileSheet::SubSheet const&getSubSheet(TileSheet const &ts, ox::SpanView<uint32_t> const &idx) noexcept {
return gfx::getSubSheet(idx, 0, ts.subsheet);
}
TileSheet::SubSheet &getSubSheet(TileSheet &ts, ox::SpanView<uint32_t> const &idx) noexcept {
return gfx::getSubSheet(idx, 0, ts.subsheet);
}
#if defined(__GNUC__) && __GNUC__ >= 13
#pragma GCC diagnostic pop
#endif
ox::Error addSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept {
auto &parent = getSubSheet(ts, idx);
if (parent.subsheets.size() < 2) {
parent.subsheets.emplace_back(++ts.idIt, ox::sfmt("Subsheet {}", parent.subsheets.size()), 1, 1);
} else {
parent.subsheets.emplace_back(++ts.idIt, "Subsheet 0", parent.columns, parent.rows);
parent.subsheets.emplace_back(++ts.idIt, "Subsheet 1", 1, 1);
}
return {};
}
ox::Error insertSubSheet(TileSheet &ts, ox::SpanView<uint32_t> const&idx, TileSheet::SubSheet ss) noexcept {
if (idx.empty()) {
return ox::Error{1, "invalid insert idx"};
}
auto &parent = getSubSheet(ts, {idx.data(), idx.size() - 1});
auto const insertIdx = idx[idx.size() - 1];
if (insertIdx > parent.subsheets.size()) {
return ox::Error{1, "invalid insert idx"};
}
parent.subsheets.emplace(insertIdx, std::move(ss));
return {};
}
ox::Error rmSubSheet(
TileSheet &ts,
TileSheet::SubSheetIdx const&idx,
std::size_t const idxIt,
TileSheet::SubSheet &pSubsheet) noexcept {
if (idxIt == idx.size() - 1) {
return pSubsheet.subsheets.erase(idx[idxIt]).error;
}
return rmSubSheet(ts, idx, idxIt + 1, pSubsheet.subsheets[idx[idxIt]]);
}
ox::Error rmSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept {
return rmSubSheet(ts, idx, 0, ts.subsheet);
}
uint8_t getPixel(
TileSheet const&ts,
ox::Point const&pt,
TileSheet::SubSheetIdx const&subsheetIdx) noexcept {
auto &s = getSubSheet(ts, subsheetIdx);
auto const idx = ptToIdx(pt, s.columns);
return getPixel(s, idx);
}
uint8_t getPixel4Bpp(
CompactTileSheet const&ts,
size_t const idx) noexcept {
oxAssert(ts.bpp == 4, "TileSheet::getPixel4Bpp: wrong bpp");
if (idx & 1) {
return ts.pixels[idx / 2] >> 4;
} else {
return ts.pixels[idx / 2] & 0b0000'1111;
}
}
uint8_t getPixel8Bpp(
CompactTileSheet const&ts,
size_t const idx) noexcept {
oxAssert(ts.bpp == 8, "TileSheet::getPixel8Bpp: wrong bpp");
return ts.pixels[idx];
}
ox::Pair<uint8_t> get2Pixels4Bpp(
CompactTileSheet const&ts,
size_t const idx) noexcept {
oxAssert(ts.bpp == 4, "TileSheet::getPixel4Bpp: wrong bpp");
auto const out = ts.pixels[idx / 2];
return {
static_cast<uint8_t>(out & 0x0f),
static_cast<uint8_t>(out >> 4),
};
}
ox::Pair<uint8_t> get2Pixels8Bpp(
CompactTileSheet const&ts,
size_t const idx) noexcept {
oxAssert(ts.bpp == 8, "TileSheet::getPixel8Bpp: wrong bpp");
return {
static_cast<uint8_t>(ts.pixels[idx]),
static_cast<uint8_t>(ts.pixels[idx + 1]),
};
}
static ox::Result<SubSheetId> getIdFor(
TileSheet::SubSheet const&ss,
ox::SpanView<ox::StringView> const&pNamePath,
std::size_t const pIt = 0) noexcept {
for (auto &sub : ss.subsheets) {
if (sub.name == pNamePath[pIt]) {
if (pIt == pNamePath.size()) {
return ss.id;
}
return getIdFor(ss, pNamePath, pIt + 1);
}
}
return ox::Error(1, "SubSheet not found");
}
ox::Result<SubSheetId> getIdFor(TileSheet const&ts, ox::StringViewCR path) noexcept {
return getIdFor(ts.subsheet, ox::split<8>(path, '.'));
}
/**
* Gets the offset in tiles of the desired subsheet.
*/
static ox::Result<uint32_t> getTileOffset(
TileSheet::SubSheet const&ss,
ox::SpanView<ox::StringView> const&pNamePath,
std::size_t const pIt = 0,
uint32_t pCurrentTotal = 0) noexcept {
// pIt == pNamePath.size() - 1 &&
if (ss.name != pNamePath[pIt]) {
return ox::Error(2, "Wrong branch");
}
if (pIt == pNamePath.size() - 1) {
return pCurrentTotal;
}
for (auto &sub : ss.subsheets) {
auto [offset, err] = getTileOffset(
sub, pNamePath, pIt + 1, pCurrentTotal);
if (!err) {
return offset;
}
// Possible bug? Should this be using a recursive version of
// pixelCnt will count pixels in subsheets of sub as well.
pCurrentTotal += pixelCnt(sub) / PixelsPerTile;
}
return ox::Error(1, "SubSheet not found");
}
ox::Result<uint32_t> getTileOffset(TileSheet const&ts, ox::StringViewCR pNamePath) noexcept {
return gfx::getTileOffset(ts.subsheet, ox::split<8>(pNamePath, '.'));
}
ox::Result<ox::StringView> getNameFor(TileSheet &ts, SubSheetId const pId) noexcept {
return gfx::getNameFor(ts.subsheet, pId);
}
ox::Result<ox::StringView> getNameFor(TileSheet const&ts, SubSheetId const pId) noexcept {
return gfx::getNameFor(ts.subsheet, pId);
}
static void readPixelsTo(TileSheet::SubSheet &ss, int const pBpp, ox::Vector<uint8_t> &pPixels) noexcept {
if (!ss.subsheets.empty()) {
for (auto &s: ss.subsheets) {
readPixelsTo(s, pBpp, pPixels);
}
} else {
if (pBpp == 4) {
for (size_t i{}; i < ss.pixels.size() - 1; i += 2) {
auto const p1 = ss.pixels[i];
auto const p2 = ss.pixels[i + 1];
pPixels.emplace_back(static_cast<uint8_t>(p1 | (p2 << 4)));
}
} else {
for (auto const p : ss.pixels) {
pPixels.emplace_back(p);
}
}
}
}
ox::Vector<uint8_t> pixels(TileSheet &ts) noexcept {
ox::Vector<uint8_t> out;
readPixelsTo(ts.subsheet, ts.bpp, out);
return out;
}
ox::Vector<uint32_t> resizeTileSheetData(
ox::Vector<uint32_t> const&srcPixels,
ox::Size const&srcSize,
int const 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;
}
}