123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353 |
- /******************************************************************************
- * Spine Runtimes Software License v2.5
- *
- * Copyright (c) 2013-2016, Esoteric Software
- * All rights reserved.
- *
- * You are granted a perpetual, non-exclusive, non-sublicensable, and
- * non-transferable license to use, install, execute, and perform the Spine
- * Runtimes software and derivative works solely for personal or internal
- * use. Without the written permission of Esoteric Software (see Section 2 of
- * the Spine Software License Agreement), you may not (a) modify, translate,
- * adapt, or develop new applications using the Spine Runtimes or otherwise
- * create derivative works or improvements of the Spine Runtimes or (b) remove,
- * delete, alter, or obscure any trademarks or any copyright, trademark, patent,
- * or other intellectual property or proprietary rights notices on or in the
- * Software, including any copy thereof. Redistributions in binary or source
- * form must include this license and terms.
- *
- * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
- * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
- * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
- #include <spine/Atlas.h>
- #include <ctype.h>
- #include <spine/extension.h>
- spAtlasPage* spAtlasPage_create (spAtlas* atlas, const char* name) {
- spAtlasPage* self = NEW(spAtlasPage);
- CONST_CAST(spAtlas*, self->atlas) = atlas;
- MALLOC_STR(self->name, name);
- return self;
- }
- void spAtlasPage_dispose (spAtlasPage* self) {
- _spAtlasPage_disposeTexture(self);
- FREE(self->name);
- FREE(self);
- }
- /**/
- spAtlasRegion* spAtlasRegion_create () {
- return NEW(spAtlasRegion);
- }
- void spAtlasRegion_dispose (spAtlasRegion* self) {
- FREE(self->name);
- FREE(self->splits);
- FREE(self->pads);
- FREE(self);
- }
- /**/
- typedef struct {
- const char* begin;
- const char* end;
- } Str;
- static void trim (Str* str) {
- while (isspace((unsigned char)*str->begin) && str->begin < str->end)
- (str->begin)++;
- if (str->begin == str->end) return;
- str->end--;
- while (isspace((unsigned char)*str->end) && str->end >= str->begin)
- str->end--;
- str->end++;
- }
- /* Tokenize string without modification. Returns 0 on failure. */
- static int readLine (const char** begin, const char* end, Str* str) {
- if (*begin == end) return 0;
- str->begin = *begin;
- /* Find next delimiter. */
- while (*begin != end && **begin != '\n')
- (*begin)++;
- str->end = *begin;
- trim(str);
- if (*begin != end) (*begin)++;
- return 1;
- }
- /* Moves str->begin past the first occurence of c. Returns 0 on failure. */
- static int beginPast (Str* str, char c) {
- const char* begin = str->begin;
- while (1) {
- char lastSkippedChar = *begin;
- if (begin == str->end) return 0;
- begin++;
- if (lastSkippedChar == c) break;
- }
- str->begin = begin;
- return 1;
- }
- /* Returns 0 on failure. */
- static int readValue (const char** begin, const char* end, Str* str) {
- readLine(begin, end, str);
- if (!beginPast(str, ':')) return 0;
- trim(str);
- return 1;
- }
- /* Returns the number of tuple values read (1, 2, 4, or 0 for failure). */
- static int readTuple (const char** begin, const char* end, Str tuple[]) {
- int i;
- Str str = {NULL, NULL};
- readLine(begin, end, &str);
- if (!beginPast(&str, ':')) return 0;
- for (i = 0; i < 3; ++i) {
- tuple[i].begin = str.begin;
- if (!beginPast(&str, ',')) break;
- tuple[i].end = str.begin - 2;
- trim(&tuple[i]);
- }
- tuple[i].begin = str.begin;
- tuple[i].end = str.end;
- trim(&tuple[i]);
- return i + 1;
- }
- static char* mallocString (Str* str) {
- int length = (int)(str->end - str->begin);
- char* string = MALLOC(char, length + 1);
- memcpy(string, str->begin, length);
- string[length] = '\0';
- return string;
- }
- static int indexOf (const char** array, int count, Str* str) {
- int length = (int)(str->end - str->begin);
- int i;
- for (i = count - 1; i >= 0; i--)
- if (strncmp(array[i], str->begin, length) == 0) return i;
- return 0;
- }
- static int equals (Str* str, const char* other) {
- return strncmp(other, str->begin, str->end - str->begin) == 0;
- }
- static int toInt (Str* str) {
- return (int)strtol(str->begin, (char**)&str->end, 10);
- }
- 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"};
- spAtlas* spAtlas_create (const char* begin, int length, const char* dir, void* rendererObject) {
- spAtlas* self;
- 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);
- page = spAtlasPage_create(self, name);
- FREE(name);
- if (lastPage)
- lastPage->next = page;
- else
- self->pages = page;
- lastPage = page;
- 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);
- if (!readTuple(&begin, end, tuple)) return abortAtlas(self);
- page->minFilter = (spAtlasFilter)indexOf(textureFilterNames, 8, tuple);
- page->magFilter = (spAtlasFilter)indexOf(textureFilterNames, 8, tuple + 1);
- if (!readValue(&begin, end, &str)) return abortAtlas(self);
- 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;
- }
- }
- _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 = mallocString(&str);
- if (!readValue(&begin, end, &str)) return abortAtlas(self);
- region->rotate = equals(&str, "true");
- 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;
- }
- if (!(count = readTuple(&begin, end, tuple))) 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);
- if (!(count = readTuple(&begin, end, tuple))) 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);
- }
- }
- return self;
- }
- spAtlas* spAtlas_createFromFile (const char* path, void* rendererObject) {
- int dirLength;
- char *dir;
- int length;
- const char* data;
- spAtlas* atlas = 0;
- /* Get directory from atlas path. */
- const char* lastForwardSlash = strrchr(path, '/');
- const char* lastBackwardSlash = strrchr(path, '\\');
- const char* lastSlash = lastForwardSlash > lastBackwardSlash ? lastForwardSlash : lastBackwardSlash;
- if (lastSlash == path) lastSlash++; /* Never drop starting slash. */
- dirLength = (int)(lastSlash ? lastSlash - path : 0);
- dir = MALLOC(char, dirLength + 1);
- memcpy(dir, path, dirLength);
- dir[dirLength] = '\0';
- data = _spUtil_readFile(path, &length);
- if (data) atlas = spAtlas_create(data, length, dir, rendererObject);
- FREE(data);
- FREE(dir);
- return atlas;
- }
- void spAtlas_dispose (spAtlas* self) {
- spAtlasRegion* region, *nextRegion;
- spAtlasPage* page = self->pages;
- while (page) {
- spAtlasPage* nextPage = page->next;
- spAtlasPage_dispose(page);
- page = nextPage;
- }
- region = self->regions;
- while (region) {
- nextRegion = region->next;
- spAtlasRegion_dispose(region);
- region = nextRegion;
- }
- FREE(self);
- }
- spAtlasRegion* spAtlas_findRegion (const spAtlas* self, const char* name) {
- spAtlasRegion* region = self->regions;
- while (region) {
- if (strcmp(region->name, name) == 0) return region;
- region = region->next;
- }
- return 0;
- }
|