15std::string trim(
const std::string &
s) {
17 while (
a <
s.size() && std::isspace(
static_cast<unsigned char>(
s[
a]))) ++
a;
19 while (
b >
a && std::isspace(
static_cast<unsigned char>(
s[
b - 1]))) --
b;
20 return s.substr(
a,
b -
a);
23bool startsWithIndent(
const std::string &
line) {
24 return !
line.empty() && (
line[0] ==
' ' ||
line[0] ==
'\t');
27bool parsePair(
const std::string &
value,
int &
a,
int &
b) {
28 auto comma =
value.find(
',');
29 if (comma == std::string::npos)
return false;
31 a = std::stoi(trim(
value.substr(0, comma)));
32 b = std::stoi(trim(
value.substr(comma + 1)));
39bool parseBool(
const std::string &
value,
bool &out) {
40 std::string
v = trim(
value);
41 for (
char &
c :
v)
c = static_cast<char>(
std::tolower(static_cast<unsigned char>(
c)));
42 if (
v ==
"true" ||
v ==
"1" ||
v ==
"yes") {
46 if (
v ==
"false" ||
v ==
"0" ||
v ==
"no") {
62void SpineAtlas::checkPage(
int index)
const {
67void SpineAtlas::checkRegion(
int index)
const {
69 throw Exception(
"SpineAtlas: region index %d out of range (count=%d)",
index,
76 regionByName_.clear();
84 auto *fs = eve::ModuleManager::getInstance<eve::filesystem::Filesystem>(
"Filesystem");
85 if (!fs) fs = eve::filesystem::Filesystem::create();
86 std::unique_ptr<eve::filesystem::FileData> data;
88 data.reset(fs->read(
path));
93 if (!data || data->getSize() == 0) {
97 std::string
text(
static_cast<const char *
>(data->getData()),
98 static_cast<size_t>(data->getSize()));
104 std::istringstream in(
text);
107 Region *cur =
nullptr;
109 auto applyKeyValue = [&](
const std::string &keyIn,
const std::string &val) {
110 std::string
key = keyIn;
111 for (
char &
c :
key)
c =
static_cast<char>(std::tolower(
static_cast<unsigned char>(
c)));
113 if (
key ==
"rotate") {
117 }
else if (
key ==
"xy") {
118 parsePair(val, cur->x, cur->y);
119 }
else if (
key ==
"size") {
120 parsePair(val, cur->w, cur->h);
121 }
else if (
key ==
"orig" ||
key ==
"originalsize") {
122 parsePair(val, cur->origW, cur->origH);
123 }
else if (
key ==
"offset") {
124 parsePair(val, cur->offsetX, cur->offsetY);
126 }
else if (pageIndex >= 0) {
127 Page &
p = pages_[
static_cast<size_t>(pageIndex)];
128 if (
key ==
"size") parsePair(val,
p.width,
p.height);
132 while (std::getline(in,
line)) {
133 if (!
line.empty() &&
line.back() ==
'\r')
line.pop_back();
134 std::string raw =
line;
135 std::string
t = trim(
line);
141 const bool indented = startsWithIndent(raw);
142 auto colon =
t.find(
':');
145 if (colon != std::string::npos && (indented || cur ==
nullptr)) {
147 if (!indented) cur =
nullptr;
148 applyKeyValue(trim(
t.substr(0, colon)), trim(
t.substr(colon + 1)));
152 if (!indented && colon == std::string::npos) {
153 std::string lower =
t;
154 for (
char &
c : lower)
c =
static_cast<char>(std::tolower(
static_cast<unsigned char>(
c)));
155 const bool looksLikeImage =
156 lower.find(
".png") != std::string::npos || lower.find(
".jpg") != std::string::npos ||
157 lower.find(
".jpeg") != std::string::npos || lower.find(
".webp") != std::string::npos ||
158 lower.find(
".ktx") != std::string::npos || lower.find(
".pvr") != std::string::npos;
160 if (pageIndex < 0 || looksLikeImage) {
164 pageIndex =
static_cast<int>(pages_.size()) - 1;
168 if (regionByName_.count(
t)) {
175 r.pageIndex = pageIndex;
176 regions_.push_back(
r);
177 regionByName_[
t] =
static_cast<int>(regions_.size()) - 1;
178 cur = ®ions_.back();
182 if (colon != std::string::npos)
183 applyKeyValue(trim(
t.substr(0, colon)), trim(
t.substr(colon + 1)));
186 for (Region &
r : regions_) {
187 if (
r.origW <= 0)
r.origW =
r.w;
188 if (
r.origH <= 0)
r.origH =
r.h;
191 if (pages_.empty()) {
200 checkPage(pageIndex);
201 return pages_[
static_cast<size_t>(pageIndex)].
name;
205 checkPage(pageIndex);
206 return pages_[
static_cast<size_t>(pageIndex)].
width;
210 checkPage(pageIndex);
211 return pages_[
static_cast<size_t>(pageIndex)].
height;
215 auto it = regionByName_.find(
name);
216 return it == regionByName_.end() ? -1 : it->second;
221 return regions_[
static_cast<size_t>(
index)].
name;
226 return regions_[
static_cast<size_t>(
index)].pageIndex;
231 return regions_[
static_cast<size_t>(
index)].
x;
236 return regions_[
static_cast<size_t>(
index)].
y;
241 return regions_[
static_cast<size_t>(
index)].
w;
246 return regions_[
static_cast<size_t>(
index)].
h;
251 return regions_[
static_cast<size_t>(
index)].origW;
256 return regions_[
static_cast<size_t>(
index)].origH;
271 return regions_[
static_cast<size_t>(
index)].rotate;
277 if (texW <= 0 || texH <= 0)
throw Exception(
"SpineAtlas.getRegionUV: invalid texture size");
278 const Region &
r = regions_[
static_cast<size_t>(
index)];
281 float x =
static_cast<float>(
r.x);
282 float y =
static_cast<float>(
r.y);
283 u0 =
x /
static_cast<float>(texW);
284 v0 =
y /
static_cast<float>(texH);
286 u1 = (
x +
static_cast<float>(
r.h)) /
static_cast<float>(texW);
287 v1 = (
y +
static_cast<float>(
r.w)) /
static_cast<float>(texH);
289 u1 = (
x +
static_cast<float>(
r.w)) /
static_cast<float>(texW);
290 v1 = (
y +
static_cast<float>(
r.h)) /
static_cast<float>(texH);
EVENGINE_API_FOUNDATION public API.
int getRegionY(int index) const
Returns the region y.
int getPageHeight(int pageIndex) const
Returns the page height.
int getRegionOriginalWidth(int index) const
Returns the region original width.
std::string getPageName(int pageIndex) const
Returns the page name.
std::string getRegionName(int index) const
Returns the region name.
bool loadFromFile(const std::string &path, std::string *error=nullptr)
Loads from file.
int getPageWidth(int pageIndex) const
Returns the page width.
void getRegionUV(int index, int texW, int texH, float &u0, float &v0, float &u1, float &v1) const
Normalized UVs for a texture of texW×texH (usually page size).
int getRegionOriginalHeight(int index) const
Returns the region original height.
int getRegionOffsetY(int index) const
Returns the region offset y.
bool getRegionRotate(int index) const
Returns the region rotate.
int getPageCount() const
Returns the page count.
int getRegionPage(int index) const
Returns the region page.
int getRegionWidth(int index) const
Returns the region width.
int getRegionX(int index) const
Returns the region x.
int getRegionHeight(int index) const
Returns the region height.
int getRegionCount() const
Returns the region count.
int findRegion(const std::string &name) const
Finds region.
bool loadFromText(const std::string &text, std::string *error=nullptr)
Loads from text.
int getRegionOffsetX(int index) const
Returns the region offset x.