From 5411717c1a8fda36604a10bed47e574d9c7d1efa Mon Sep 17 00:00:00 2001 From: badlogic Date: Wed, 12 May 2021 15:18:39 +0200 Subject: [PATCH] [c] 4.0 porting, texture atlas parsing and key value storage in regions. --- spine-c/spine-c/include/spine/Atlas.h | 12 +- spine-c/spine-c/src/spine/Atlas.c | 332 ++++++++++++++---------- spine-cpp/spine-cpp/src/spine/Atlas.cpp | 2 +- 3 files changed, 208 insertions(+), 138 deletions(-) diff --git a/spine-c/spine-c/include/spine/Atlas.h b/spine-c/spine-c/include/spine/Atlas.h index d950a9c64..4b11087de 100644 --- a/spine-c/spine-c/include/spine/Atlas.h +++ b/spine-c/spine-c/include/spine/Atlas.h @@ -31,6 +31,7 @@ #define SPINE_ATLAS_H_ #include +#include #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; diff --git a/spine-c/spine-c/src/spine/Atlas.c b/spine-c/spine-c/src/spine/Atlas.c index b73c1f59e..503adc556 100644 --- a/spine-c/spine-c/src/spine/Atlas.c +++ b/spine-c/spine-c/src/spine/Atlas.c @@ -31,6 +31,63 @@ #include #include +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; } diff --git a/spine-cpp/spine-cpp/src/spine/Atlas.cpp b/spine-cpp/spine-cpp/src/spine/Atlas.cpp index 1f2a1ec96..a290b0576 100644 --- a/spine-cpp/spine-cpp/src/spine/Atlas.cpp +++ b/spine-cpp/spine-cpp/src/spine/Atlas.cpp @@ -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")) {