mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-20 09:16:01 +08:00
[c] 4.0 porting, texture atlas parsing and key value storage in regions.
This commit is contained in:
parent
7ee6b78da6
commit
5411717c1a
@ -31,6 +31,7 @@
|
||||
#define SPINE_ATLAS_H_
|
||||
|
||||
#include <spine/dll.h>
|
||||
#include <spine/Array.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -84,6 +85,13 @@ struct spAtlasPage {
|
||||
SP_API spAtlasPage* spAtlasPage_create (spAtlas* atlas, const char* name);
|
||||
SP_API void spAtlasPage_dispose (spAtlasPage* self);
|
||||
|
||||
/**/
|
||||
typedef struct spKeyValue {
|
||||
char *name;
|
||||
float values[5];
|
||||
} spKeyValue;
|
||||
_SP_ARRAY_DECLARE_TYPE(spKeyValueArray, spKeyValue)
|
||||
|
||||
/**/
|
||||
typedef struct spAtlasRegion spAtlasRegion;
|
||||
struct spAtlasRegion {
|
||||
@ -96,9 +104,7 @@ struct spAtlasRegion {
|
||||
int degrees;
|
||||
int* splits;
|
||||
int* pads;
|
||||
char** names;
|
||||
float* values;
|
||||
int numValues;
|
||||
spKeyValueArray *keyValues;
|
||||
|
||||
spAtlasPage* page;
|
||||
|
||||
|
||||
@ -31,6 +31,63 @@
|
||||
#include <ctype.h>
|
||||
#include <spine/extension.h>
|
||||
|
||||
spKeyValueArray *spKeyValueArray_create(int initialCapacity) {
|
||||
spKeyValueArray *array = ((spKeyValueArray *) _spCalloc(1, sizeof(spKeyValueArray), "_file_name_", 39));
|
||||
array->size = 0;
|
||||
array->capacity = initialCapacity;
|
||||
array->items = ((spKeyValue *) _spCalloc(initialCapacity, sizeof(spKeyValue), "_file_name_", 39));
|
||||
return array;
|
||||
}
|
||||
void spKeyValueArray_dispose(spKeyValueArray *self) {
|
||||
_spFree((void *) self->items);
|
||||
_spFree((void *) self);
|
||||
}
|
||||
void spKeyValueArray_clear(spKeyValueArray *self) { self->size = 0; }
|
||||
spKeyValueArray *spKeyValueArray_setSize(spKeyValueArray *self, int newSize) {
|
||||
self->size = newSize;
|
||||
if (self->capacity < newSize) {
|
||||
self->capacity = ((8) > ((int) (self->size * 1.75f)) ? (8) : ((int) (self->size * 1.75f)));
|
||||
self->items = ((spKeyValue *) _spRealloc(self->items, sizeof(spKeyValue) * (self->capacity)));
|
||||
}
|
||||
return self;
|
||||
}
|
||||
void spKeyValueArray_ensureCapacity(spKeyValueArray *self, int newCapacity) {
|
||||
if (self->capacity >= newCapacity)return;
|
||||
self->capacity = newCapacity;
|
||||
self->items = ((spKeyValue *) _spRealloc(self->items, sizeof(spKeyValue) * (self->capacity)));
|
||||
}
|
||||
void spKeyValueArray_add(spKeyValueArray *self, spKeyValue value) {
|
||||
if (self->size == self->capacity) {
|
||||
self->capacity = ((8) > ((int) (self->size * 1.75f)) ? (8) : ((int) (self->size * 1.75f)));
|
||||
self->items = ((spKeyValue *) _spRealloc(self->items, sizeof(spKeyValue) * (self->capacity)));
|
||||
}
|
||||
self->items[self->size++] = value;
|
||||
}
|
||||
void spKeyValueArray_addAll(spKeyValueArray *self, spKeyValueArray *other) {
|
||||
int i = 0;
|
||||
for (; i < other->size; i++) { spKeyValueArray_add(self, other->items[i]); }
|
||||
}
|
||||
void spKeyValueArray_addAllValues(spKeyValueArray *self, spKeyValue *values, int offset, int count) {
|
||||
int i = offset, n = offset + count;
|
||||
for (; i < n; i++) { spKeyValueArray_add(self, values[i]); }
|
||||
}
|
||||
void spKeyValueArray_removeAt(spKeyValueArray *self, int index) {
|
||||
self->size--;
|
||||
__builtin___memmove_chk(self->items + index, self->items + index + 1, sizeof(spKeyValue) * (self->size - index),
|
||||
__builtin_object_size(self->items + index, 0));
|
||||
}
|
||||
int spKeyValueArray_contains(spKeyValueArray *self, spKeyValue value) {
|
||||
spKeyValue *items = self->items;
|
||||
int i, n;
|
||||
for (i = 0, n = self->size; i < n; i++) { if (!strcmp(items[i].name, value.name))return -1; }
|
||||
return 0;
|
||||
}
|
||||
spKeyValue spKeyValueArray_pop(spKeyValueArray *self) {
|
||||
spKeyValue item = self->items[--self->size];
|
||||
return item;
|
||||
}
|
||||
spKeyValue spKeyValueArray_peek(spKeyValueArray *self) { return self->items[self->size - 1]; }
|
||||
|
||||
spAtlasPage* spAtlasPage_create(spAtlas* atlas, const char* name) {
|
||||
spAtlasPage* self = NEW(spAtlasPage);
|
||||
CONST_CAST(spAtlas*, self->atlas) = atlas;
|
||||
@ -47,19 +104,20 @@ void spAtlasPage_dispose(spAtlasPage* self) {
|
||||
/**/
|
||||
|
||||
spAtlasRegion* spAtlasRegion_create() {
|
||||
return NEW(spAtlasRegion);
|
||||
spAtlasRegion *region = NEW(spAtlasRegion);
|
||||
region->keyValues = spKeyValueArray_create(2);
|
||||
return region;
|
||||
}
|
||||
|
||||
void spAtlasRegion_dispose(spAtlasRegion* self) {
|
||||
int i, n;
|
||||
int i, n;
|
||||
FREE(self->name);
|
||||
FREE(self->splits);
|
||||
FREE(self->pads);
|
||||
for (i = 0, n = self->numValues; i < n; i++) {
|
||||
FREE(self->names[i]);
|
||||
}
|
||||
FREE(self->names);
|
||||
FREE(self->values);
|
||||
for (i = 0, n = self->keyValues->size; i < n; i++) {
|
||||
FREE(self->keyValues->items[i].name);
|
||||
}
|
||||
spKeyValueArray_dispose(self->keyValues);
|
||||
FREE(self);
|
||||
}
|
||||
|
||||
@ -183,156 +241,162 @@ static int ai_readEntry(SimpleString entry[5], SimpleString *line) {
|
||||
}
|
||||
}
|
||||
|
||||
static spAtlas* abortAtlas(spAtlas* self) {
|
||||
spAtlas_dispose(self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *formatNames[] = {"", "Alpha", "Intensity", "LuminanceAlpha", "RGB565", "RGBA4444", "RGB888",
|
||||
"RGBA8888"};
|
||||
static const char *textureFilterNames[] = {"", "Nearest", "Linear", "MipMap", "MipMapNearestNearest",
|
||||
"MipMapLinearNearest",
|
||||
"MipMapNearestLinear", "MipMapLinearLinear"};
|
||||
|
||||
int indexOf(const char **array, int count, SimpleString *str) {
|
||||
int i;
|
||||
for (i = 0; i < count; i++)
|
||||
if (ss_equals(str, array[i])) return i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
spAtlas* spAtlas_create(const char* begin, int length, const char* dir, void* rendererObject) {
|
||||
spAtlas* self;
|
||||
AtlasInput reader;
|
||||
SimpleString *line;
|
||||
SimpleString entry[5];
|
||||
spAtlasPage *page = NULL;
|
||||
spAtlasPage *lastPage = NULL;
|
||||
spAtlasRegion *lastRegion = NULL;
|
||||
|
||||
int count;
|
||||
const char* end = begin + length;
|
||||
int dirLength = (int)strlen(dir);
|
||||
int needsSlash = dirLength > 0 && dir[dirLength - 1] != '/' && dir[dirLength - 1] != '\\';
|
||||
|
||||
spAtlasPage *page = 0;
|
||||
spAtlasPage *lastPage = 0;
|
||||
spAtlasRegion *lastRegion = 0;
|
||||
Str str;
|
||||
Str tuple[4];
|
||||
|
||||
self = NEW(spAtlas);
|
||||
self->rendererObject = rendererObject;
|
||||
|
||||
while (readLine(&begin, end, &str)) {
|
||||
if (str.end - str.begin == 0)
|
||||
page = 0;
|
||||
else if (!page) {
|
||||
char* name = mallocString(&str);
|
||||
char* path = MALLOC(char, dirLength + needsSlash + strlen(name) + 1);
|
||||
memcpy(path, dir, dirLength);
|
||||
if (needsSlash) path[dirLength] = '/';
|
||||
strcpy(path + dirLength + needsSlash, name);
|
||||
reader.start = begin;
|
||||
reader.end = begin + length;
|
||||
reader.index = (char*)begin;
|
||||
reader.length = length;
|
||||
|
||||
page = spAtlasPage_create(self, name);
|
||||
FREE(name);
|
||||
if (lastPage)
|
||||
lastPage->next = page;
|
||||
else
|
||||
self->pages = page;
|
||||
lastPage = page;
|
||||
line = ai_readLine(&reader);
|
||||
while (line != NULL && line->length == 0)
|
||||
line = ai_readLine(&reader);
|
||||
|
||||
switch (readTuple(&begin, end, tuple)) {
|
||||
case 0:
|
||||
return abortAtlas(self);
|
||||
case 2: /* size is only optional for an atlas packed with an old TexturePacker. */
|
||||
page->width = toInt(tuple);
|
||||
page->height = toInt(tuple + 1);
|
||||
if (!readTuple(&begin, end, tuple)) return abortAtlas(self);
|
||||
}
|
||||
page->format = (spAtlasFormat)indexOf(formatNames, 8, tuple);
|
||||
while (-1) {
|
||||
if (line == NULL || line->length == 0) break;
|
||||
if (ai_readEntry(entry, line) == 0) break;
|
||||
line = ai_readLine(&reader);
|
||||
}
|
||||
|
||||
if (!readTuple(&begin, end, tuple)) return abortAtlas(self);
|
||||
page->minFilter = (spAtlasFilter)indexOf(textureFilterNames, 8, tuple);
|
||||
page->magFilter = (spAtlasFilter)indexOf(textureFilterNames, 8, tuple + 1);
|
||||
while (-1) {
|
||||
if (line == NULL) break;
|
||||
if (ss_trim(line)->length == 0) {
|
||||
page = NULL;
|
||||
line = ai_readLine(&reader);
|
||||
} else if (page == NULL) {
|
||||
char *name = ss_copy(line);
|
||||
char *path = CALLOC(char, dirLength + needsSlash + strlen(name) + 1);
|
||||
memcpy(path, dir, dirLength);
|
||||
if (needsSlash) path[dirLength] = '/';
|
||||
strcpy(path + dirLength + needsSlash, name);
|
||||
page = spAtlasPage_create(self, name);
|
||||
FREE(name);
|
||||
|
||||
if (!readValue(&begin, end, &str)) return abortAtlas(self);
|
||||
if (lastPage)
|
||||
lastPage->next = page;
|
||||
else
|
||||
self->pages = page;
|
||||
lastPage = page;
|
||||
|
||||
page->uWrap = SP_ATLAS_CLAMPTOEDGE;
|
||||
page->vWrap = SP_ATLAS_CLAMPTOEDGE;
|
||||
if (!equals(&str, "none")) {
|
||||
if (str.end - str.begin == 1) {
|
||||
if (*str.begin == 'x')
|
||||
page->uWrap = SP_ATLAS_REPEAT;
|
||||
else if (*str.begin == 'y')
|
||||
page->vWrap = SP_ATLAS_REPEAT;
|
||||
}
|
||||
else if (equals(&str, "xy")) {
|
||||
page->uWrap = SP_ATLAS_REPEAT;
|
||||
page->vWrap = SP_ATLAS_REPEAT;
|
||||
}
|
||||
}
|
||||
while (-1) {
|
||||
line = ai_readLine(&reader);
|
||||
if (ai_readEntry(entry, line) == 0) break;
|
||||
if (ss_equals(&entry[0], "size")) {
|
||||
page->width = ss_toInt(&entry[1]);
|
||||
page->height = ss_toInt(&entry[2]);
|
||||
} else if (ss_equals(&entry[0], "format")) {
|
||||
page->format = (spAtlasFormat) indexOf(formatNames, 8, &entry[1]);
|
||||
} else if (ss_equals(&entry[0], "filter")) {
|
||||
page->minFilter = (spAtlasFilter) indexOf(textureFilterNames, 8, &entry[1]);
|
||||
page->magFilter = (spAtlasFilter) indexOf(textureFilterNames, 8, &entry[2]);
|
||||
} else if (ss_equals(&entry[0], "repeat")) {
|
||||
page->uWrap = SP_ATLAS_CLAMPTOEDGE;
|
||||
page->vWrap = SP_ATLAS_CLAMPTOEDGE;
|
||||
if (ss_indexOf(&entry[1], 'x') != -1) page->uWrap = SP_ATLAS_REPEAT;
|
||||
if (ss_indexOf(&entry[1], 'y') != -1) page->vWrap = SP_ATLAS_REPEAT;
|
||||
} else if (ss_equals(&entry[0], "pma")) {
|
||||
page->pma = ss_equals(&entry[1], "true");
|
||||
}
|
||||
}
|
||||
|
||||
_spAtlasPage_createTexture(page, path);
|
||||
FREE(path);
|
||||
} else {
|
||||
spAtlasRegion *region = spAtlasRegion_create();
|
||||
if (lastRegion)
|
||||
lastRegion->next = region;
|
||||
else
|
||||
self->regions = region;
|
||||
lastRegion = region;
|
||||
_spAtlasPage_createTexture(page, path);
|
||||
FREE(path);
|
||||
} else {
|
||||
spAtlasRegion *region = spAtlasRegion_create();
|
||||
if (lastRegion)
|
||||
lastRegion->next = region;
|
||||
else
|
||||
self->regions = region;
|
||||
lastRegion = region;
|
||||
region->page = page;
|
||||
region->name = ss_copy(line);
|
||||
while (-1) {
|
||||
line = ai_readLine(&reader);
|
||||
count = ai_readEntry(entry, line);
|
||||
if (count == 0) break;
|
||||
if (ss_equals(&entry[0], "xy")) {
|
||||
region->x = ss_toInt(&entry[1]);
|
||||
region->y = ss_toInt(&entry[2]);
|
||||
} else if (ss_equals(&entry[0], "size")) {
|
||||
region->width = ss_toInt(&entry[1]);
|
||||
region->height = ss_toInt(&entry[2]);
|
||||
} else if (ss_equals(&entry[0], "bounds")) {
|
||||
region->x = ss_toInt(&entry[1]);
|
||||
region->y = ss_toInt(&entry[2]);
|
||||
region->width = ss_toInt(&entry[3]);
|
||||
region->height = ss_toInt(&entry[4]);
|
||||
} else if (ss_equals(&entry[0], "offset")) {
|
||||
region->offsetX = ss_toInt(&entry[1]);
|
||||
region->offsetY = ss_toInt(&entry[2]);
|
||||
} else if (ss_equals(&entry[0], "orig")) {
|
||||
region->originalWidth = ss_toInt(&entry[1]);
|
||||
region->originalHeight = ss_toInt(&entry[2]);
|
||||
} else if (ss_equals(&entry[0], "offsets")) {
|
||||
region->offsetX = ss_toInt(&entry[1]);
|
||||
region->offsetY = ss_toInt(&entry[2]);
|
||||
region->originalWidth = ss_toInt(&entry[3]);
|
||||
region->originalHeight = ss_toInt(&entry[4]);
|
||||
} else if (ss_equals(&entry[0], "rotate")) {
|
||||
if (ss_equals(&entry[1], "true")) {
|
||||
region->degrees = 90;
|
||||
} else if (!ss_equals(&entry[1], "false")) {
|
||||
region->degrees = ss_toInt(&entry[1]);
|
||||
}
|
||||
} else if (ss_equals(&entry[0], "index")) {
|
||||
region->index = ss_toInt(&entry[1]);
|
||||
} else {
|
||||
int i = 0;
|
||||
spKeyValue keyValue;
|
||||
keyValue.name = ss_copy(&entry[0]);
|
||||
for (i = 0; i < count; i++) {
|
||||
keyValue.values[i] = ss_toInt(&entry[i + 1]);
|
||||
}
|
||||
spKeyValueArray_add(region->keyValues, keyValue);
|
||||
}
|
||||
}
|
||||
if (region->originalWidth == 0 && region->originalHeight == 0) {
|
||||
region->originalWidth = region->width;
|
||||
region->originalHeight = region->height;
|
||||
}
|
||||
|
||||
region->page = page;
|
||||
region->name = mallocString(&str);
|
||||
|
||||
if (!readValue(&begin, end, &str)) return abortAtlas(self);
|
||||
if (equals(&str, "true"))
|
||||
region->degrees = 90;
|
||||
else if (equals(&str, "false"))
|
||||
region->degrees = 0;
|
||||
else
|
||||
region->degrees = toInt(&str);
|
||||
region->rotate = region->degrees == 90;
|
||||
|
||||
if (readTuple(&begin, end, tuple) != 2) return abortAtlas(self);
|
||||
region->x = toInt(tuple);
|
||||
region->y = toInt(tuple + 1);
|
||||
|
||||
if (readTuple(&begin, end, tuple) != 2) return abortAtlas(self);
|
||||
region->width = toInt(tuple);
|
||||
region->height = toInt(tuple + 1);
|
||||
|
||||
region->u = region->x / (float)page->width;
|
||||
region->v = region->y / (float)page->height;
|
||||
if (region->rotate) {
|
||||
region->u2 = (region->x + region->height) / (float)page->width;
|
||||
region->v2 = (region->y + region->width) / (float)page->height;
|
||||
} else {
|
||||
region->u2 = (region->x + region->width) / (float)page->width;
|
||||
region->v2 = (region->y + region->height) / (float)page->height;
|
||||
}
|
||||
|
||||
count = readTuple(&begin, end, tuple);
|
||||
if (!count) return abortAtlas(self);
|
||||
if (count == 4) { /* split is optional */
|
||||
region->splits = MALLOC(int, 4);
|
||||
region->splits[0] = toInt(tuple);
|
||||
region->splits[1] = toInt(tuple + 1);
|
||||
region->splits[2] = toInt(tuple + 2);
|
||||
region->splits[3] = toInt(tuple + 3);
|
||||
|
||||
count = readTuple(&begin, end, tuple);
|
||||
if (!count) return abortAtlas(self);
|
||||
if (count == 4) { /* pad is optional, but only present with splits */
|
||||
region->pads = MALLOC(int, 4);
|
||||
region->pads[0] = toInt(tuple);
|
||||
region->pads[1] = toInt(tuple + 1);
|
||||
region->pads[2] = toInt(tuple + 2);
|
||||
region->pads[3] = toInt(tuple + 3);
|
||||
|
||||
if (!readTuple(&begin, end, tuple)) return abortAtlas(self);
|
||||
}
|
||||
}
|
||||
|
||||
region->originalWidth = toInt(tuple);
|
||||
region->originalHeight = toInt(tuple + 1);
|
||||
|
||||
readTuple(&begin, end, tuple);
|
||||
region->offsetX = toInt(tuple);
|
||||
region->offsetY = toInt(tuple + 1);
|
||||
|
||||
if (!readValue(&begin, end, &str)) return abortAtlas(self);
|
||||
region->index = toInt(&str);
|
||||
}
|
||||
}
|
||||
region->u = (float)region->x / page->width;
|
||||
region->v = (float)region->y / page->height;
|
||||
if (region->degrees == 90) {
|
||||
region->u2 = (float)(region->x + region->height) / page->width;
|
||||
region->v2 = (float)(region->y + region->width) / page->height;
|
||||
} else {
|
||||
region->u2 = (float)(region->x + region->width) / page->width;
|
||||
region->v2 = (float)(region->y + region->height) / page->height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@ -292,7 +292,7 @@ void Atlas::load(const char *begin, int length, const char *dir, bool createText
|
||||
region->page = page;
|
||||
region->name = String(line->copy(), true);
|
||||
while (true) {
|
||||
line = line = reader.readLine();
|
||||
line = reader.readLine();
|
||||
int count = reader.readEntry(entry, line);
|
||||
if (count == 0) break;
|
||||
if (entry[0].equals("xy")) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user