Squashed 'deps/nostalgia/' changes from 830f8fe3..672b92b3
672b92b3 [nostalgia/gfx/studio] Remove accidental version tag in default Palette 762a6517 [nostalgia] Rename core to gfx d141154a Merge commit '38777cfac8868b3628332090260710d5ac26aba0' 6170647c [nostalgia,studio] Proper fix for input filtering 48e45c7d [studio] Cleanup 5d3d9229 [nostalgia/core/studio/paletteeditor] Ignore keyboard input when popup is open d54e93d8 [studio] Cleanup 7b638538 Merge commit '8e0b6ffbabb10f8a6e9ad7e9f07e0ba1d039a02e' 240effd3 Merge commit '7e20f7200963cd0b22f84cc46e10db12b6c13806' f6f2acd6 [nostalgia/core/studio/tilesheeteditor] Add back file type check for palette drop git-subtree-dir: deps/nostalgia git-subtree-split: 672b92b363a2047c4c8ce93fb3d88001a76da35f
This commit is contained in:
164
src/nostalgia/modules/gfx/include/nostalgia/gfx/color.hpp
Normal file
164
src/nostalgia/modules/gfx/include/nostalgia/gfx/color.hpp
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/math.hpp>
|
||||
#include <ox/std/types.hpp>
|
||||
|
||||
namespace nostalgia::gfx {
|
||||
|
||||
using Color16 = uint16_t;
|
||||
|
||||
/**
|
||||
* Nostalgia Core logically uses 16 bit colors, but must translate that to 32
|
||||
* bit colors in some implementations.
|
||||
*/
|
||||
using Color32 = uint32_t;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Color32 toColor32(Color16 nc) noexcept {
|
||||
const auto r = static_cast<Color32>(((nc & 0b0000000000011111) >> 0) * 8);
|
||||
const auto g = static_cast<Color32>(((nc & 0b0000001111100000) >> 5) * 8);
|
||||
const auto b = static_cast<Color32>(((nc & 0b0111110000000000) >> 10) * 8);
|
||||
const auto a = static_cast<Color32>(255);
|
||||
return r | (g << 8) | (b << 16) | (a << 24);
|
||||
}
|
||||
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr uint8_t red16(Color16 c) noexcept {
|
||||
return c & 0b0000000000011111;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr uint8_t green16(Color16 c) noexcept {
|
||||
return (c & 0b0000001111100000) >> 5;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr uint8_t blue16(Color16 c) noexcept {
|
||||
return (c & 0b0111110000000000) >> 10;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr uint8_t alpha16(Color16 c) noexcept {
|
||||
return static_cast<uint8_t>(c >> 15);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr uint8_t red32(Color16 c) noexcept {
|
||||
return red16(c) * 8;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr uint8_t green32(Color16 c) noexcept {
|
||||
return green16(c) * 8;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr uint8_t blue32(Color16 c) noexcept {
|
||||
return blue16(c) * 8;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr uint8_t alpha32(Color16 c) noexcept {
|
||||
return static_cast<uint8_t>((c >> 15) * 255);
|
||||
}
|
||||
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr uint8_t red32(Color32 c) noexcept {
|
||||
return (c & 0x000000ff) >> 0;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr uint8_t green32(Color32 c) noexcept {
|
||||
return (c & 0x0000ff00) >> 8;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr uint8_t blue32(Color32 c) noexcept {
|
||||
return (c & 0x00ff0000) >> 16;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Color32 color32(uint8_t r, uint8_t g, uint8_t b) noexcept {
|
||||
return static_cast<Color32>(r | (g << 8) | (b << 16));
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Color32 color32(Color16 c) noexcept {
|
||||
return color32(red32(c), green32(c), blue32(c));
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Color32 color32(float r, float g, float b) noexcept {
|
||||
return static_cast<Color32>(static_cast<uint8_t>(r * 255) | (static_cast<uint8_t>(g * 255) << 8) | (static_cast<uint8_t>(b * 255) << 16));
|
||||
}
|
||||
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr float redf(Color16 c) noexcept {
|
||||
return static_cast<float>(red16(c)) / 31.f;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr float greenf(Color16 c) noexcept {
|
||||
return static_cast<float>(green16(c)) / 31.f;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr float bluef(Color16 c) noexcept {
|
||||
return static_cast<float>(blue16(c)) / 31.f;
|
||||
}
|
||||
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr float redf(Color32 c) noexcept {
|
||||
return static_cast<float>(red32(c)) / 255.f;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr float greenf(Color32 c) noexcept {
|
||||
return static_cast<float>(green32(c)) / 255.f;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr float bluef(Color32 c) noexcept {
|
||||
return static_cast<float>(blue32(c)) / 255.f;
|
||||
}
|
||||
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Color16 color16(int r, int g, int b, int a = 0) noexcept {
|
||||
return static_cast<Color16>(ox::min<uint8_t>(static_cast<uint8_t>(r), 31))
|
||||
| static_cast<Color16>(ox::min<uint8_t>(static_cast<uint8_t>(g), 31) << 5)
|
||||
| static_cast<Color16>(ox::min<uint8_t>(static_cast<uint8_t>(b), 31) << 10)
|
||||
| static_cast<Color16>(a << 15);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Color16 color16(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 0) noexcept {
|
||||
return static_cast<Color16>(ox::min<uint8_t>(r, 31))
|
||||
| static_cast<Color16>(ox::min<uint8_t>(g, 31) << 5)
|
||||
| static_cast<Color16>(ox::min<uint8_t>(b, 31) << 10)
|
||||
| static_cast<Color16>(a << 15);
|
||||
}
|
||||
|
||||
static_assert(color16(0, 31, 0) == 992);
|
||||
static_assert(color16(16, 31, 0) == 1008);
|
||||
static_assert(color16(16, 31, 8) == 9200);
|
||||
static_assert(color16(16, 32, 8) == 9200);
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Color16 applySelectionColor(Color16 const color) noexcept {
|
||||
namespace core = nostalgia::gfx;
|
||||
auto const r = core::red16(color) / 2;
|
||||
auto const g = (core::green16(color) + 20) / 2;
|
||||
auto const b = (core::blue16(color) + 31) / 2;
|
||||
return core::color16(r, g, b);
|
||||
}
|
||||
|
||||
}
|
18
src/nostalgia/modules/gfx/include/nostalgia/gfx/consts.hpp
Normal file
18
src/nostalgia/modules/gfx/include/nostalgia/gfx/consts.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/stringliteral.hpp>
|
||||
|
||||
namespace nostalgia::gfx {
|
||||
|
||||
constexpr auto TileWidth = 8;
|
||||
constexpr auto TileHeight = 8;
|
||||
constexpr auto PixelsPerTile = TileWidth * TileHeight;
|
||||
|
||||
constexpr ox::StringLiteral FileExt_ng("ng");
|
||||
constexpr ox::StringLiteral FileExt_npal("npal");
|
||||
|
||||
}
|
31
src/nostalgia/modules/gfx/include/nostalgia/gfx/context.hpp
Normal file
31
src/nostalgia/modules/gfx/include/nostalgia/gfx/context.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/fs/fs.hpp>
|
||||
#include <ox/model/desctypes.hpp>
|
||||
#include <ox/std/buffer.hpp>
|
||||
#include <ox/std/size.hpp>
|
||||
|
||||
#include <turbine/context.hpp>
|
||||
|
||||
#include "initparams.hpp"
|
||||
|
||||
namespace nostalgia::gfx {
|
||||
|
||||
class Context;
|
||||
|
||||
void safeDelete(Context *ctx) noexcept;
|
||||
|
||||
using ContextUPtr = ox::UPtr<Context>;
|
||||
|
||||
ox::Result<ContextUPtr> init(turbine::Context &tctx, InitParams const¶ms = {}) noexcept;
|
||||
|
||||
keel::Context &keelCtx(Context &ctx) noexcept;
|
||||
|
||||
turbine::Context &turbineCtx(Context &ctx) noexcept;
|
||||
|
||||
}
|
||||
|
14
src/nostalgia/modules/gfx/include/nostalgia/gfx/core.hpp
Normal file
14
src/nostalgia/modules/gfx/include/nostalgia/gfx/core.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "color.hpp"
|
||||
#include "context.hpp"
|
||||
#include "gfx.hpp"
|
||||
#include "initparams.hpp"
|
||||
#include "keelmodule.hpp"
|
||||
#include "palette.hpp"
|
||||
#include "ptidxconv.hpp"
|
||||
#include "tilesheet.hpp"
|
256
src/nostalgia/modules/gfx/include/nostalgia/gfx/gfx.hpp
Normal file
256
src/nostalgia/modules/gfx/include/nostalgia/gfx/gfx.hpp
Normal file
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/array.hpp>
|
||||
#include <ox/std/size.hpp>
|
||||
#include <ox/std/types.hpp>
|
||||
#include <ox/model/def.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
#include "palette.hpp"
|
||||
#include "tilesheet.hpp"
|
||||
|
||||
namespace nostalgia::gfx {
|
||||
|
||||
struct Sprite {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.Sprite";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
bool enabled = false;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
unsigned tileIdx = 0;
|
||||
unsigned spriteShape = 0;
|
||||
unsigned spriteSize = 0;
|
||||
unsigned flipX = 0;
|
||||
unsigned bpp = 0;
|
||||
/**
|
||||
* Valid priorities: 0-3
|
||||
*/
|
||||
unsigned priority = 0;
|
||||
};
|
||||
|
||||
OX_MODEL_BEGIN(Sprite)
|
||||
OX_MODEL_FIELD(idx)
|
||||
OX_MODEL_FIELD(x)
|
||||
OX_MODEL_FIELD(y)
|
||||
OX_MODEL_FIELD(enabled)
|
||||
OX_MODEL_FIELD(tileIdx)
|
||||
OX_MODEL_FIELD(spriteShape)
|
||||
OX_MODEL_FIELD(spriteSize)
|
||||
OX_MODEL_FIELD(flipX)
|
||||
OX_MODEL_FIELD(bpp)
|
||||
OX_MODEL_FIELD(priority)
|
||||
OX_MODEL_END()
|
||||
|
||||
struct BgTile {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.BgTile";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
unsigned tileIdx = 0;
|
||||
unsigned palBank = 0;
|
||||
unsigned flipX = false;
|
||||
unsigned flipY = false;
|
||||
};
|
||||
|
||||
OX_MODEL_BEGIN(BgTile)
|
||||
OX_MODEL_FIELD(tileIdx)
|
||||
OX_MODEL_FIELD(palBank)
|
||||
OX_MODEL_FIELD(horizontalFlip)
|
||||
OX_MODEL_FIELD(verticalFlip)
|
||||
OX_MODEL_END()
|
||||
|
||||
struct TileSheetSetEntrySection {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheetSetEntrySection";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
int32_t begin = 0;
|
||||
int32_t tiles = 0;
|
||||
[[nodiscard]]
|
||||
constexpr auto end() const noexcept {
|
||||
return begin + tiles - 1;
|
||||
}
|
||||
};
|
||||
|
||||
OX_MODEL_BEGIN(TileSheetSetEntrySection)
|
||||
OX_MODEL_FIELD(begin)
|
||||
OX_MODEL_FIELD(tiles)
|
||||
OX_MODEL_END()
|
||||
|
||||
struct TileSheetSetEntry {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheetSetEntry";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
ox::FileAddress tilesheet;
|
||||
ox::Vector<TileSheetSetEntrySection> sections;
|
||||
};
|
||||
|
||||
OX_MODEL_BEGIN(TileSheetSetEntry)
|
||||
OX_MODEL_FIELD(tilesheet)
|
||||
OX_MODEL_FIELD(sections)
|
||||
OX_MODEL_END()
|
||||
|
||||
struct TileSheetSet {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheetSet";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
static constexpr auto Preloadable = true;
|
||||
int32_t bpp = 0;
|
||||
ox::Vector<TileSheetSetEntry> entries;
|
||||
};
|
||||
|
||||
OX_MODEL_BEGIN(TileSheetSet)
|
||||
OX_MODEL_FIELD(bpp)
|
||||
OX_MODEL_FIELD(entries)
|
||||
OX_MODEL_END()
|
||||
|
||||
[[nodiscard]]
|
||||
int tileColumns(Context&) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
int tileRows(Context&) noexcept;
|
||||
|
||||
ox::Error loadBgPalette(
|
||||
Context &ctx,
|
||||
size_t palBank,
|
||||
CompactPalette const&palette,
|
||||
size_t page = 0) noexcept;
|
||||
|
||||
ox::Error loadSpritePalette(
|
||||
Context &ctx,
|
||||
CompactPalette const&palette,
|
||||
size_t page = 0) noexcept;
|
||||
|
||||
ox::Error loadBgPalette(
|
||||
Context &ctx,
|
||||
size_t palBank,
|
||||
ox::StringViewCR palettePath) noexcept;
|
||||
|
||||
ox::Error loadBgPalette(
|
||||
Context &ctx,
|
||||
size_t palBank,
|
||||
ox::FileAddress const&paletteAddr) noexcept;
|
||||
|
||||
ox::Error loadSpritePalette(
|
||||
Context &ctx,
|
||||
ox::StringViewCR palettePath) noexcept;
|
||||
|
||||
ox::Error loadSpritePalette(
|
||||
Context &ctx,
|
||||
ox::FileAddress const&paletteAddr) noexcept;
|
||||
|
||||
ox::Error loadBgTileSheet(
|
||||
Context &ctx,
|
||||
unsigned cbb,
|
||||
TileSheetSet const&set) noexcept;
|
||||
|
||||
void clearCbb(Context &ctx, unsigned cbb) noexcept;
|
||||
|
||||
void clearCbbs(Context &ctx) noexcept;
|
||||
|
||||
ox::Error loadBgTileSheet(
|
||||
Context &ctx,
|
||||
unsigned cbb,
|
||||
CompactTileSheet const&ts,
|
||||
size_t dstTileIdx,
|
||||
size_t srcTileIdx,
|
||||
size_t tileCnt) noexcept;
|
||||
|
||||
ox::Error loadBgTileSheet(
|
||||
Context &ctx,
|
||||
unsigned cbb,
|
||||
ox::StringViewCR tsPath,
|
||||
size_t dstTileIdx,
|
||||
size_t srcTileIdx,
|
||||
size_t tileCnt) noexcept;
|
||||
|
||||
ox::Error loadBgTileSheet(
|
||||
Context &ctx,
|
||||
unsigned cbb,
|
||||
ox::FileAddress const&tsAddr,
|
||||
size_t dstTileIdx,
|
||||
size_t srcTileIdx,
|
||||
size_t tileCnt) noexcept;
|
||||
|
||||
ox::Error loadBgTileSheet(
|
||||
Context &ctx,
|
||||
unsigned cbb,
|
||||
CompactTileSheet const&ts,
|
||||
ox::Optional<unsigned> const&paletteBank = {}) noexcept;
|
||||
|
||||
ox::Error loadBgTileSheet(
|
||||
Context &ctx,
|
||||
unsigned cbb,
|
||||
ox::StringViewCR tilesheetPath,
|
||||
ox::Optional<unsigned> const&paletteBank) noexcept;
|
||||
|
||||
ox::Error loadBgTileSheet(
|
||||
Context &ctx,
|
||||
unsigned cbb,
|
||||
ox::FileAddress const&tilesheetAddr,
|
||||
ox::Optional<unsigned> const&paletteBank = {}) noexcept;
|
||||
|
||||
ox::Error loadSpriteTileSheet(
|
||||
Context &ctx,
|
||||
CompactTileSheet const&ts,
|
||||
bool loadDefaultPalette) noexcept;
|
||||
|
||||
ox::Error loadSpriteTileSheet(
|
||||
Context &ctx,
|
||||
ox::StringViewCR tilesheetPath,
|
||||
bool loadDefaultPalette = false) noexcept;
|
||||
|
||||
ox::Error loadSpriteTileSheet(
|
||||
Context &ctx,
|
||||
ox::FileAddress const&tilesheetAddr,
|
||||
bool loadDefaultPalette = false) noexcept;
|
||||
|
||||
ox::Error loadSpriteTileSheet(
|
||||
Context &ctx,
|
||||
TileSheetSet const&set) noexcept;
|
||||
|
||||
void setBgTile(Context &ctx, uint_t bgIdx, int column, int row, unsigned tile, unsigned palBank = 0) noexcept;
|
||||
|
||||
void setBgTile(Context &ctx, uint_t bgIdx, int column, int row, BgTile const&tile) noexcept;
|
||||
|
||||
void clearBg(Context &ctx, uint_t bgIdx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t bgStatus(Context &ctx) noexcept;
|
||||
|
||||
void setBgStatus(Context &ctx, uint32_t status) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
bool bgStatus(Context &ctx, unsigned bg) noexcept;
|
||||
|
||||
void setBgStatus(Context &ctx, unsigned bg, bool status) noexcept;
|
||||
|
||||
void setBgCbb(Context &ctx, unsigned bgIdx, unsigned cbb) noexcept;
|
||||
|
||||
void setBgPriority(Context &ctx, uint_t bgIdx, uint_t priority) noexcept;
|
||||
|
||||
void hideSprite(Context &ctx, unsigned) noexcept;
|
||||
|
||||
void showSprite(Context &ctx, unsigned) noexcept;
|
||||
|
||||
void setSprite(Context &c, uint_t idx, Sprite const&s) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint_t spriteCount(Context &ctx) noexcept;
|
||||
|
||||
ox::Error initConsole(Context &ctx) noexcept;
|
||||
|
||||
void puts(Context &ctx, int column, int row, ox::StringViewCR str) noexcept;
|
||||
|
||||
}
|
||||
|
||||
namespace nostalgia::gfx::gl {
|
||||
|
||||
constexpr ox::CStringView GlslVersion = "#version 330";
|
||||
|
||||
[[nodiscard]]
|
||||
ox::Size drawSize(int scale = 5) noexcept;
|
||||
|
||||
void draw(gfx::Context &ctx, ox::Size const&renderSz) noexcept;
|
||||
|
||||
void draw(gfx::Context&, int scale = 5) noexcept;
|
||||
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/types.hpp>
|
||||
|
||||
namespace nostalgia::gfx {
|
||||
|
||||
struct InitParams {
|
||||
bool glInstallDrawer = true;
|
||||
uint_t glSpriteCount = 128;
|
||||
uint_t glBlocksPerSprite = 64;
|
||||
};
|
||||
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <keel/module.hpp>
|
||||
|
||||
namespace nostalgia::gfx {
|
||||
|
||||
const keel::Module *keelModule() noexcept;
|
||||
|
||||
}
|
289
src/nostalgia/modules/gfx/include/nostalgia/gfx/palette.hpp
Normal file
289
src/nostalgia/modules/gfx/include/nostalgia/gfx/palette.hpp
Normal file
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/vector.hpp>
|
||||
#include <ox/model/def.hpp>
|
||||
|
||||
#include <turbine/turbine.hpp>
|
||||
|
||||
#include "color.hpp"
|
||||
|
||||
namespace nostalgia::gfx {
|
||||
|
||||
struct PaletteColorV1 {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.PaletteColor";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
uint8_t r{}, g{}, b{}, a{};
|
||||
constexpr PaletteColorV1() noexcept = default;
|
||||
constexpr PaletteColorV1(Color16 const c) noexcept:
|
||||
r{red16(c)},
|
||||
g{green16(c)},
|
||||
b{blue16(c)},
|
||||
a{alpha16(c)} {}
|
||||
constexpr operator Color16() const noexcept { return color16(r, g, b, a); }
|
||||
};
|
||||
|
||||
OX_MODEL_BEGIN(PaletteColorV1)
|
||||
OX_MODEL_FIELD(r)
|
||||
OX_MODEL_FIELD(g)
|
||||
OX_MODEL_FIELD(b)
|
||||
OX_MODEL_FIELD(a)
|
||||
OX_MODEL_END()
|
||||
|
||||
using PaletteColor = PaletteColorV1;
|
||||
|
||||
|
||||
struct PalettePageV1 {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.Palette.PalettePage";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
ox::String name;
|
||||
ox::Vector<PaletteColorV1> colors;
|
||||
constexpr PalettePageV1() noexcept = default;
|
||||
constexpr PalettePageV1(ox::StringParam pName, ox::Vector<PaletteColorV1> pColors) noexcept:
|
||||
name(std::move(pName)), colors(std::move(pColors)) {}
|
||||
constexpr PalettePageV1(ox::StringParam pName, ox::Vector<Color16> const&pColors) noexcept:
|
||||
name(std::move(pName)) {
|
||||
colors.reserve(pColors.size());
|
||||
for (auto const c : pColors) {
|
||||
colors.emplace_back(c);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
OX_MODEL_BEGIN(PalettePageV1)
|
||||
OX_MODEL_FIELD(name)
|
||||
OX_MODEL_FIELD(colors)
|
||||
OX_MODEL_END()
|
||||
|
||||
using PalettePage = PalettePageV1;
|
||||
|
||||
|
||||
struct NostalgiaPalette {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.NostalgiaPalette";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
ox::Vector<Color16> colors = {};
|
||||
};
|
||||
|
||||
OX_MODEL_BEGIN(NostalgiaPalette)
|
||||
OX_MODEL_FIELD(colors)
|
||||
OX_MODEL_END()
|
||||
|
||||
|
||||
struct PaletteV1 {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.Palette";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
ox::Vector<Color16> colors;
|
||||
};
|
||||
|
||||
OX_MODEL_BEGIN(PaletteV1)
|
||||
OX_MODEL_FIELD(colors)
|
||||
OX_MODEL_END()
|
||||
|
||||
|
||||
struct PaletteV2 {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.Palette";
|
||||
static constexpr auto TypeVersion = 2;
|
||||
static constexpr auto Preloadable = true;
|
||||
ox::Vector<ox::Vector<Color16>> pages;
|
||||
};
|
||||
|
||||
OX_MODEL_BEGIN(PaletteV2)
|
||||
OX_MODEL_FIELD(pages)
|
||||
OX_MODEL_END()
|
||||
|
||||
|
||||
struct PaletteV3 {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.Palette";
|
||||
static constexpr auto TypeVersion = 3;
|
||||
static constexpr auto Preloadable = true;
|
||||
struct ColorInfo {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.Palette.ColorInfo";
|
||||
static constexpr auto TypeVersion = 3;
|
||||
ox::String name;
|
||||
constexpr ColorInfo() noexcept = default;
|
||||
constexpr ColorInfo(ox::StringParam pName) noexcept: name{std::move(pName)} {}
|
||||
};
|
||||
ox::Vector<ColorInfo> colorInfo;
|
||||
ox::Vector<ox::Vector<Color16>> pages;
|
||||
};
|
||||
|
||||
OX_MODEL_BEGIN(PaletteV3::ColorInfo)
|
||||
OX_MODEL_FIELD(name)
|
||||
OX_MODEL_END()
|
||||
|
||||
OX_MODEL_BEGIN(PaletteV3)
|
||||
OX_MODEL_FIELD(colorInfo)
|
||||
OX_MODEL_FIELD(pages)
|
||||
OX_MODEL_END()
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool valid(PaletteV3 const&p) noexcept {
|
||||
auto const colors = p.colorInfo.size();
|
||||
return ox::all_of(p.pages.begin(), p.pages.end(), [colors](auto const&page) {
|
||||
return page.size() == colors;
|
||||
});
|
||||
}
|
||||
|
||||
constexpr ox::Error repair(PaletteV3 &p) noexcept {
|
||||
auto const colors = p.colorInfo.size();
|
||||
for (auto &page : p.pages) {
|
||||
page.resize(colors);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
struct PaletteV4 {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.Palette";
|
||||
static constexpr auto TypeVersion = 4;
|
||||
static constexpr auto Preloadable = true;
|
||||
ox::Vector<ox::String> colorNames;
|
||||
ox::Vector<PalettePageV1> pages;
|
||||
};
|
||||
|
||||
OX_MODEL_BEGIN(PaletteV4)
|
||||
OX_MODEL_FIELD(colorNames)
|
||||
OX_MODEL_FIELD(pages)
|
||||
OX_MODEL_END()
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool valid(PaletteV4 const&p) noexcept {
|
||||
auto const colors = p.colorNames.size();
|
||||
return ox::all_of(p.pages.begin(), p.pages.end(), [colors](PalettePageV1 const&page) {
|
||||
return page.colors.size() == colors;
|
||||
});
|
||||
}
|
||||
|
||||
constexpr ox::Error repair(PaletteV4 &p) noexcept {
|
||||
auto const colors = p.colorNames.size();
|
||||
for (auto &page : p.pages) {
|
||||
page.colors.resize(colors);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
using Palette = PaletteV4;
|
||||
|
||||
|
||||
struct CompactPaletteV1 {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.CompactPalette";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
static constexpr auto Preloadable = true;
|
||||
ox::Vector<ox::Vector<Color16>> pages{};
|
||||
};
|
||||
|
||||
OX_MODEL_BEGIN(CompactPaletteV1)
|
||||
OX_MODEL_FIELD(pages)
|
||||
OX_MODEL_END()
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool valid(CompactPaletteV1 const&p) noexcept {
|
||||
size_t colors{};
|
||||
for (auto const&page : p.pages) {
|
||||
colors = ox::max(colors, page.size());
|
||||
}
|
||||
return ox::all_of(p.pages.begin(), p.pages.end(), [colors](ox::Vector<Color16> const&page) {
|
||||
return page.size() == colors;
|
||||
});
|
||||
}
|
||||
|
||||
constexpr ox::Error repair(CompactPaletteV1 &p) noexcept {
|
||||
size_t colors{};
|
||||
for (auto const&page : p.pages) {
|
||||
colors = ox::max(colors, page.size());
|
||||
}
|
||||
for (auto &page : p.pages) {
|
||||
page.resize(colors);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
using CompactPalette = CompactPaletteV1;
|
||||
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Color16 color(Palette const&pal, size_t page, size_t idx) noexcept {
|
||||
if (page < pal.pages.size() && idx < pal.pages[page].colors.size()) [[likely]] {
|
||||
return pal.pages[page].colors[idx];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Color16 color(CompactPalette const&pal, size_t page, size_t idx) noexcept {
|
||||
if (page < pal.pages.size() && idx < pal.pages[page].size()) [[likely]] {
|
||||
return pal.pages[page][idx];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Color16 color(Palette const&pal, size_t idx) noexcept {
|
||||
return color(pal, 0, idx);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Color16 color(CompactPalette const&pal, size_t idx) noexcept {
|
||||
return color(pal, 0, idx);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto &colors(Palette &pal, size_t page = 0) noexcept {
|
||||
return pal.pages[page].colors;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto &colors(CompactPalette &pal, size_t page = 0) noexcept {
|
||||
return pal.pages[page];
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto &colors(Palette const&pal, size_t page = 0) noexcept {
|
||||
return pal.pages[page];
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto &colors(CompactPalette const&pal, size_t page = 0) noexcept {
|
||||
return pal.pages[page];
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr size_t colorCnt(Palette const&pal, size_t page = 0) noexcept {
|
||||
if (page < pal.pages.size()) [[likely]] {
|
||||
return pal.pages[page].colors.size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr size_t colorCnt(CompactPalette const&pal, size_t page = 0) noexcept {
|
||||
if (page < pal.pages.size()) [[likely]] {
|
||||
return pal.pages[page].size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr size_t largestPage(Palette const&pal) noexcept {
|
||||
size_t out{};
|
||||
for (auto const&page : pal.pages) {
|
||||
out = ox::max(out, page.colors.size());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr size_t largestPage(CompactPalette const&pal) noexcept {
|
||||
size_t out{};
|
||||
for (auto const&page : pal.pages) {
|
||||
out = ox::max(out, page.size());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/point.hpp>
|
||||
|
||||
#include "consts.hpp"
|
||||
|
||||
namespace nostalgia::gfx {
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr std::size_t ptToIdx(int x, int y, int c, int scale = 1) noexcept {
|
||||
const auto tileWidth = TileWidth * scale;
|
||||
const auto tileHeight = TileHeight * scale;
|
||||
const auto pixelsPerTile = tileWidth * tileHeight;
|
||||
const auto colLength = static_cast<std::size_t>(pixelsPerTile);
|
||||
const auto rowLength = static_cast<std::size_t>(static_cast<std::size_t>(c / tileWidth) * colLength);
|
||||
const auto colStart = static_cast<std::size_t>(colLength * static_cast<std::size_t>(x / tileWidth));
|
||||
const auto rowStart = static_cast<std::size_t>(rowLength * static_cast<std::size_t>(y / tileHeight));
|
||||
const auto colOffset = static_cast<std::size_t>(x % tileWidth);
|
||||
const auto rowOffset = static_cast<std::size_t>((y % tileHeight) * tileHeight);
|
||||
return static_cast<std::size_t>(colStart + colOffset + rowStart + rowOffset);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr std::size_t ptToIdx(const ox::Point &pt, int c, int scale = 1) noexcept {
|
||||
return ptToIdx(pt.x, pt.y, c * TileWidth, scale);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr ox::Point idxToPt(int i, int c, int scale = 1) noexcept {
|
||||
const auto tileWidth = TileWidth * scale;
|
||||
const auto tileHeight = TileHeight * scale;
|
||||
const auto pixelsPerTile = tileWidth * tileHeight;
|
||||
// prevent divide by zeros
|
||||
if (!c) {
|
||||
++c;
|
||||
}
|
||||
const auto t = i / pixelsPerTile; // tile number
|
||||
const auto iti = i % pixelsPerTile; // in tile index
|
||||
const auto tc = t % c; // tile column
|
||||
const auto tr = t / c; // tile row
|
||||
const auto itx = iti % tileWidth; // in tile x
|
||||
const auto ity = iti / tileHeight; // in tile y
|
||||
return {
|
||||
itx + tc * tileWidth,
|
||||
ity + tr * tileHeight,
|
||||
};
|
||||
}
|
||||
|
||||
static_assert(idxToPt(4, 1) == ox::Point{4, 0});
|
||||
static_assert(idxToPt(8, 1) == ox::Point{0, 1});
|
||||
static_assert(idxToPt(8, 2) == ox::Point{0, 1});
|
||||
static_assert(idxToPt(64, 2) == ox::Point{8, 0});
|
||||
static_assert(idxToPt(128, 2) == ox::Point{0, 8});
|
||||
static_assert(idxToPt(129, 2) == ox::Point{1, 8});
|
||||
static_assert(idxToPt(192, 2) == ox::Point{8, 8});
|
||||
static_assert(idxToPt(384, 8) == ox::Point{48, 0});
|
||||
|
||||
}
|
11
src/nostalgia/modules/gfx/include/nostalgia/gfx/studio.hpp
Normal file
11
src/nostalgia/modules/gfx/include/nostalgia/gfx/studio.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <studio/studio.hpp>
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <studio/studio.hpp>
|
||||
|
||||
namespace nostalgia::gfx {
|
||||
|
||||
const studio::Module *studioModule() noexcept;
|
||||
|
||||
}
|
536
src/nostalgia/modules/gfx/include/nostalgia/gfx/tilesheet.hpp
Normal file
536
src/nostalgia/modules/gfx/include/nostalgia/gfx/tilesheet.hpp
Normal file
@@ -0,0 +1,536 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/fs/fs.hpp>
|
||||
#include <ox/std/array.hpp>
|
||||
#include <ox/std/point.hpp>
|
||||
#include <ox/std/size.hpp>
|
||||
#include <ox/std/span.hpp>
|
||||
#include <ox/std/types.hpp>
|
||||
#include <ox/model/def.hpp>
|
||||
|
||||
#include <nostalgia/gfx/ptidxconv.hpp>
|
||||
|
||||
#include "palette.hpp"
|
||||
|
||||
namespace nostalgia::gfx {
|
||||
|
||||
struct SubSheetTemplate {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.gfx.SubSheetTemplate";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
ox::String name;
|
||||
int32_t width{};
|
||||
int32_t height{};
|
||||
ox::Vector<SubSheetTemplate> subsheets;
|
||||
};
|
||||
|
||||
OX_MODEL_BEGIN(SubSheetTemplate)
|
||||
OX_MODEL_FIELD(name)
|
||||
OX_MODEL_FIELD(width)
|
||||
OX_MODEL_FIELD(height)
|
||||
OX_MODEL_FIELD(subsheets)
|
||||
OX_MODEL_END()
|
||||
|
||||
|
||||
// Predecessor to TileSheet, kept for backward compatibility
|
||||
struct TileSheetV1 {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.NostalgiaGraphic";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
int8_t bpp = 0;
|
||||
// rows and columns are really only used by TileSheetEditor
|
||||
int rows = 1;
|
||||
int columns = 1;
|
||||
ox::FileAddress defaultPalette;
|
||||
PaletteV1 pal;
|
||||
ox::Vector<uint8_t> pixels = {};
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool valid(TileSheetV1 const&ts) noexcept {
|
||||
auto const bytes = static_cast<size_t>(ts.columns * ts.rows * PixelsPerTile) / (ts.bpp == 4 ? 2 : 1);
|
||||
return (ts.bpp == 4 || ts.bpp == 8) && ts.pixels.size() == bytes;
|
||||
}
|
||||
|
||||
constexpr ox::Error repair(TileSheetV1 &ts, int bpp) noexcept {
|
||||
auto const bytes = static_cast<size_t>(ts.columns * ts.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1);
|
||||
ts.pixels.resize(bytes);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
struct TileSheetV2 {
|
||||
using SubSheetIdx = ox::Vector<std::size_t, 4>;
|
||||
|
||||
struct SubSheet {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheet.SubSheet";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
ox::String name;
|
||||
int columns = 0;
|
||||
int rows = 0;
|
||||
ox::Vector<SubSheet> subsheets;
|
||||
ox::Vector<uint8_t> pixels;
|
||||
constexpr SubSheet() noexcept = default;
|
||||
constexpr SubSheet(ox::StringParam pName, int pColumns, int pRows, int bpp) noexcept:
|
||||
name(std::move(pName)),
|
||||
columns(pColumns),
|
||||
rows(pRows),
|
||||
pixels(static_cast<size_t>(columns * rows * PixelsPerTile) / (bpp == 4 ? 2u : 1u)) {
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheet";
|
||||
static constexpr auto TypeVersion = 2;
|
||||
int8_t bpp = 4;
|
||||
ox::FileAddress defaultPalette;
|
||||
SubSheet subsheet{"Root", 1, 1, bpp};
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool valid(TileSheetV2::SubSheet const&ss, int bpp) noexcept {
|
||||
auto const bytes = static_cast<size_t>(ss.columns * ss.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1);
|
||||
return ox::all_of(ss.subsheets.begin(), ss.subsheets.end(),
|
||||
[bpp, bytes](TileSheetV2::SubSheet const&s) {
|
||||
return bytes == s.pixels.size() && valid(s, bpp);
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool valid(TileSheetV2 const&ts) noexcept {
|
||||
return (ts.bpp == 4 || ts.bpp == 8) && valid(ts.subsheet, ts.bpp);
|
||||
}
|
||||
|
||||
constexpr void repair(TileSheetV2::SubSheet &ss, int bpp) noexcept {
|
||||
auto const bytes = static_cast<size_t>(ss.columns * ss.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1);
|
||||
ss.pixels.resize(bytes);
|
||||
for (auto &s : ss.subsheets) {
|
||||
repair(s, bpp);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ox::Error repair(TileSheetV2 &ts) noexcept {
|
||||
if (ts.bpp != 4 && ts.bpp != 8) {
|
||||
return ox::Error(1, "Unable to repair TileSheet");
|
||||
}
|
||||
repair(ts.subsheet, ts.bpp);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
using SubSheetId = int32_t;
|
||||
|
||||
struct TileSheetV3 {
|
||||
using SubSheetIdx = ox::Vector<std::size_t, 4>;
|
||||
|
||||
struct SubSheet {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheet.SubSheet";
|
||||
static constexpr auto TypeVersion = 3;
|
||||
SubSheetId id = 0;
|
||||
ox::String name;
|
||||
int columns = 0;
|
||||
int rows = 0;
|
||||
ox::Vector<SubSheet> subsheets;
|
||||
ox::Vector<uint8_t> pixels;
|
||||
constexpr SubSheet() noexcept = default;
|
||||
SubSheet(
|
||||
SubSheetId pId,
|
||||
ox::StringParam pName,
|
||||
int pColumns,
|
||||
int pRows,
|
||||
int bpp) noexcept:
|
||||
id(pId),
|
||||
name(std::move(pName)),
|
||||
columns(pColumns),
|
||||
rows(pRows),
|
||||
pixels(static_cast<std::size_t>(columns * rows * PixelsPerTile) / (bpp == 4 ? 2u : 1u)) {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheet";
|
||||
static constexpr auto TypeVersion = 3;
|
||||
int8_t bpp = 4;
|
||||
SubSheetId idIt = 0;
|
||||
ox::FileAddress defaultPalette;
|
||||
SubSheet subsheet{0, "Root", 1, 1, bpp};
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool valid(TileSheetV3::SubSheet const&ss, int bpp) noexcept {
|
||||
auto const bytes = static_cast<size_t>(ss.columns * ss.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1);
|
||||
return ox::all_of(ss.subsheets.begin(), ss.subsheets.end(),
|
||||
[bpp, bytes](TileSheetV3::SubSheet const&s) {
|
||||
return bytes == s.pixels.size() && valid(s, bpp);
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool valid(TileSheetV3 const&ts) noexcept {
|
||||
return (ts.bpp == 4 || ts.bpp == 8) && valid(ts.subsheet, ts.bpp);
|
||||
}
|
||||
|
||||
constexpr void repair(TileSheetV3::SubSheet &ss, int bpp) noexcept {
|
||||
auto const bytes = static_cast<size_t>(ss.columns * ss.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1);
|
||||
ss.pixels.resize(bytes);
|
||||
for (auto &s : ss.subsheets) {
|
||||
repair(s, bpp);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ox::Error repair(TileSheetV3 &ts) noexcept {
|
||||
if (ts.bpp != 4 && ts.bpp != 8) {
|
||||
return ox::Error(1, "Unable to repair TileSheet");
|
||||
}
|
||||
repair(ts.subsheet, ts.bpp);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
struct TileSheetV4 {
|
||||
using SubSheetIdx = ox::Vector<std::size_t, 4>;
|
||||
|
||||
struct SubSheet {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheet.SubSheet";
|
||||
static constexpr auto TypeVersion = 4;
|
||||
SubSheetId id = 0;
|
||||
ox::String name;
|
||||
int columns = 0;
|
||||
int rows = 0;
|
||||
ox::Vector<SubSheet> subsheets;
|
||||
ox::Vector<uint8_t> pixels;
|
||||
|
||||
constexpr SubSheet() noexcept = default;
|
||||
SubSheet(
|
||||
SubSheetId pId,
|
||||
ox::StringParam pName,
|
||||
int pColumns,
|
||||
int pRows,
|
||||
int bpp) noexcept:
|
||||
id(pId),
|
||||
name(std::move(pName)),
|
||||
columns(pColumns),
|
||||
rows(pRows),
|
||||
pixels(static_cast<std::size_t>(columns * rows * PixelsPerTile) / (bpp == 4 ? 2u : 1u)) {
|
||||
}
|
||||
SubSheet(
|
||||
SubSheetId pId,
|
||||
ox::StringParam pName,
|
||||
int pColumns,
|
||||
int pRows,
|
||||
ox::Vector<uint8_t> pPixels) noexcept:
|
||||
id(pId),
|
||||
name(std::move(pName)),
|
||||
columns(pColumns),
|
||||
rows(pRows),
|
||||
pixels(std::move(pPixels)) {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the dimensional size of the SubSheet (e.g. width * height)
|
||||
*/
|
||||
[[nodiscard]]
|
||||
constexpr std::size_t size() const noexcept {
|
||||
return static_cast<std::size_t>(columns) * static_cast<std::size_t>(rows);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheet";
|
||||
static constexpr auto TypeVersion = 4;
|
||||
int8_t bpp = 4;
|
||||
SubSheetId idIt = 0;
|
||||
ox::FileAddress defaultPalette;
|
||||
SubSheet subsheet{0, "Root", 1, 1, bpp};
|
||||
|
||||
constexpr TileSheetV4() noexcept = default;
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool valid(TileSheetV4::SubSheet const&ss, int bpp) noexcept {
|
||||
auto const bytes = static_cast<size_t>(ss.columns * ss.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1);
|
||||
return
|
||||
(ss.pixels.empty() || ss.subsheets.empty()) &&
|
||||
ox::all_of(ss.subsheets.begin(), ss.subsheets.end(),
|
||||
[bpp, bytes](TileSheetV4::SubSheet const&s) {
|
||||
return bytes == s.pixels.size() && valid(s, bpp);
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool valid(TileSheetV4 const&ts) noexcept {
|
||||
return (ts.bpp == 4 || ts.bpp == 8) && valid(ts.subsheet, ts.bpp);
|
||||
}
|
||||
|
||||
constexpr void repair(TileSheetV4::SubSheet &ss, int bpp) noexcept {
|
||||
if (ss.subsheets.empty()) {
|
||||
auto const bytes = static_cast<size_t>(ss.columns * ss.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1);
|
||||
ss.pixels.resize(bytes);
|
||||
} else {
|
||||
ss.pixels.clear();
|
||||
ss.columns = -1;
|
||||
ss.rows = -1;
|
||||
}
|
||||
for (auto &s : ss.subsheets) {
|
||||
repair(s, bpp);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ox::Error repair(TileSheetV4 &ts) noexcept {
|
||||
if (ts.bpp != 4 && ts.bpp != 8) {
|
||||
return ox::Error(1, "Unable to repair TileSheet");
|
||||
}
|
||||
repair(ts.subsheet, ts.bpp);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
using TileSheet = TileSheetV4;
|
||||
|
||||
[[nodiscard]]
|
||||
std::size_t idx(TileSheet::SubSheet const&ss, ox::Point const&pt) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
size_t getTileCnt(TileSheet const&ts) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
TileSheet::SubSheet const*getSubsheet(TileSheet const&ts, SubSheetId id) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
ox::Optional<size_t> getTileIdx(TileSheet const&ts, SubSheetId id) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel4Bpp(TileSheet::SubSheet const&ss, std::size_t idx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel8Bpp(TileSheet::SubSheet const&ss, std::size_t idx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel(TileSheet::SubSheet const&ss, int8_t pBpp, std::size_t idx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel4Bpp(TileSheet::SubSheet const&ss, ox::Point const&pt) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel8Bpp(TileSheet::SubSheet const&ss, ox::Point const&pt) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel(TileSheet::SubSheet const&ss, int8_t pBpp, ox::Point const&pt) noexcept;
|
||||
|
||||
constexpr void walkPixels(TileSheet::SubSheet const&ss, int8_t pBpp, auto callback) noexcept {
|
||||
if (pBpp == 4) {
|
||||
const auto pixelCnt = ox::min<std::size_t>(
|
||||
static_cast<std::size_t>(ss.columns * ss.rows * PixelsPerTile) / 2,
|
||||
ss.pixels.size());
|
||||
//oxAssert(pixels.size() == pixelCnt, "Pixel count does not match rows and columns");
|
||||
for (std::size_t i = 0; i < pixelCnt; ++i) {
|
||||
const auto colorIdx1 = static_cast<uint8_t>(ss.pixels[i] & 0xF);
|
||||
const auto colorIdx2 = static_cast<uint8_t>(ss.pixels[i] >> 4);
|
||||
callback(i * 2 + 0, colorIdx1);
|
||||
callback(i * 2 + 1, colorIdx2);
|
||||
}
|
||||
} else {
|
||||
const auto pixelCnt = ox::min<std::size_t>(
|
||||
static_cast<std::size_t>(ss.columns * ss.rows * PixelsPerTile),
|
||||
ss.pixels.size());
|
||||
for (std::size_t i = 0; i < pixelCnt; ++i) {
|
||||
const auto p = ss.pixels[i];
|
||||
callback(i, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setPixel(TileSheet::SubSheet &ss, int8_t pBpp, uint64_t idx, uint8_t palIdx) noexcept;
|
||||
|
||||
void setPixel(TileSheet::SubSheet &ss, int8_t pBpp, ox::Point const&pt, uint8_t palIdx) noexcept;
|
||||
|
||||
ox::Error setPixelCount(TileSheet::SubSheet &ss, int8_t pBpp, std::size_t cnt) noexcept;
|
||||
|
||||
/**
|
||||
* Gets a count of the pixels in this sheet, and not that of its children.
|
||||
* @param pBpp bits per pixel, need for knowing how to count the pixels
|
||||
* @return a count of the pixels in this sheet
|
||||
*/
|
||||
[[nodiscard]]
|
||||
unsigned pixelCnt(TileSheet::SubSheet const&ss, int8_t pBpp) noexcept;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param ss
|
||||
* @param pBpp
|
||||
* @param sz size of Subsheet in tiles (not pixels)
|
||||
*/
|
||||
ox::Error resizeSubsheet(TileSheet::SubSheet &ss, int8_t pBpp, ox::Size const&sz) noexcept;
|
||||
|
||||
/**
|
||||
* validateSubSheetIdx takes a SubSheetIdx and moves the index to the
|
||||
* preceding or parent sheet if the current corresponding sheet does
|
||||
* not exist.
|
||||
* @param idx SubSheetIdx to validate and correct
|
||||
* @return a valid version of idx
|
||||
*/
|
||||
[[nodiscard]]
|
||||
TileSheet::SubSheetIdx validateSubSheetIdx(TileSheet const&ts, TileSheet::SubSheetIdx idx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
TileSheet::SubSheet const&getSubSheet(
|
||||
TileSheet::SubSheetIdx const&idx,
|
||||
std::size_t idxIt,
|
||||
TileSheet::SubSheet const&pSubsheet) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
TileSheet::SubSheet &getSubSheet(
|
||||
TileSheet::SubSheetIdx const&idx,
|
||||
std::size_t idxIt,
|
||||
TileSheet::SubSheet &pSubsheet) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
TileSheet::SubSheet const&getSubSheet(TileSheet const&ts, TileSheet::SubSheetIdx const&idx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
TileSheet::SubSheet &getSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept;
|
||||
|
||||
ox::Error addSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept;
|
||||
|
||||
ox::Error rmSubSheet(
|
||||
TileSheet &ts,
|
||||
TileSheet::SubSheetIdx const&idx,
|
||||
std::size_t idxIt,
|
||||
TileSheet::SubSheet &pSubsheet) noexcept;
|
||||
|
||||
ox::Error rmSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel4Bpp(
|
||||
TileSheet const&ts,
|
||||
ox::Point const&pt,
|
||||
TileSheet::SubSheetIdx const&subsheetIdx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel8Bpp(
|
||||
TileSheet const&ts,
|
||||
ox::Point const&pt,
|
||||
TileSheet::SubSheetIdx const&subsheetIdx) noexcept;
|
||||
|
||||
ox::Result<SubSheetId> getIdFor(TileSheet const&ts, ox::StringViewCR path) noexcept;
|
||||
|
||||
ox::Result<unsigned> getTileOffset(TileSheet const&ts, ox::StringViewCR pNamePath) noexcept;
|
||||
|
||||
ox::Result<uint32_t> getTileOffset(TileSheet const&ts, SubSheetId pId) noexcept;
|
||||
|
||||
ox::Result<ox::StringView> getNameFor(TileSheet::SubSheet const&ss, SubSheetId pId) noexcept;
|
||||
|
||||
ox::Result<ox::StringView> getNameFor(TileSheet const&ts, SubSheetId pId) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
ox::Vector<uint8_t> pixels(TileSheet &ts) noexcept;
|
||||
|
||||
|
||||
struct CompactTileSheetV1 {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.CompactTileSheet";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
static constexpr auto Preloadable = true;
|
||||
int8_t bpp = 0;
|
||||
ox::FileAddress defaultPalette;
|
||||
ox::Vector<uint8_t> pixels;
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool valid(CompactTileSheetV1 const&ts) noexcept {
|
||||
return ts.bpp == 4 || ts.bpp == 8;
|
||||
}
|
||||
|
||||
|
||||
using CompactTileSheet = CompactTileSheetV1;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel4Bpp(
|
||||
CompactTileSheet const&ts,
|
||||
size_t idx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel8Bpp(
|
||||
CompactTileSheet const&ts,
|
||||
size_t idx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
ox::Pair<uint8_t> get2Pixels4Bpp(
|
||||
CompactTileSheet const&ts,
|
||||
size_t idx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
ox::Pair<uint8_t> get2Pixels8Bpp(
|
||||
CompactTileSheet const&ts,
|
||||
size_t idx) noexcept;
|
||||
|
||||
OX_MODEL_BEGIN(TileSheetV1)
|
||||
OX_MODEL_FIELD(bpp)
|
||||
OX_MODEL_FIELD(rows)
|
||||
OX_MODEL_FIELD(columns)
|
||||
OX_MODEL_FIELD(defaultPalette)
|
||||
OX_MODEL_FIELD(pal)
|
||||
OX_MODEL_FIELD(pixels)
|
||||
OX_MODEL_END()
|
||||
|
||||
OX_MODEL_BEGIN(TileSheetV2::SubSheet)
|
||||
OX_MODEL_FIELD(name)
|
||||
OX_MODEL_FIELD(rows)
|
||||
OX_MODEL_FIELD(columns)
|
||||
OX_MODEL_FIELD(subsheets)
|
||||
OX_MODEL_FIELD(pixels)
|
||||
OX_MODEL_END()
|
||||
|
||||
OX_MODEL_BEGIN(TileSheetV2)
|
||||
OX_MODEL_FIELD(bpp)
|
||||
OX_MODEL_FIELD(defaultPalette)
|
||||
OX_MODEL_FIELD(subsheet)
|
||||
OX_MODEL_END()
|
||||
|
||||
OX_MODEL_BEGIN(TileSheetV3::SubSheet)
|
||||
OX_MODEL_FIELD(name)
|
||||
OX_MODEL_FIELD(rows)
|
||||
OX_MODEL_FIELD(columns)
|
||||
OX_MODEL_FIELD(subsheets)
|
||||
OX_MODEL_FIELD(pixels)
|
||||
OX_MODEL_END()
|
||||
|
||||
OX_MODEL_BEGIN(TileSheetV3)
|
||||
OX_MODEL_FIELD(bpp)
|
||||
OX_MODEL_FIELD(idIt)
|
||||
OX_MODEL_FIELD(defaultPalette)
|
||||
OX_MODEL_FIELD(subsheet)
|
||||
OX_MODEL_END()
|
||||
|
||||
OX_MODEL_BEGIN(TileSheetV4::SubSheet)
|
||||
OX_MODEL_FIELD(id)
|
||||
OX_MODEL_FIELD(name)
|
||||
OX_MODEL_FIELD(rows)
|
||||
OX_MODEL_FIELD(columns)
|
||||
OX_MODEL_FIELD(subsheets)
|
||||
OX_MODEL_FIELD(pixels)
|
||||
OX_MODEL_END()
|
||||
|
||||
OX_MODEL_BEGIN(TileSheetV4)
|
||||
OX_MODEL_FIELD(bpp)
|
||||
OX_MODEL_FIELD(idIt)
|
||||
OX_MODEL_FIELD(defaultPalette)
|
||||
OX_MODEL_FIELD(subsheet)
|
||||
OX_MODEL_END()
|
||||
|
||||
OX_MODEL_BEGIN(CompactTileSheetV1)
|
||||
OX_MODEL_FIELD(bpp)
|
||||
OX_MODEL_FIELD(defaultPalette)
|
||||
OX_MODEL_FIELD(pixels)
|
||||
OX_MODEL_END()
|
||||
|
||||
ox::Vector<uint32_t> resizeTileSheetData(
|
||||
ox::Vector<uint32_t> const&srcPixels,
|
||||
ox::Size const&srcSize,
|
||||
int scale = 2) noexcept;
|
||||
|
||||
}
|
Reference in New Issue
Block a user