| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338 |
- /******************************************************************************
- * Spine Runtimes Software License
- * Version 2
- *
- * Copyright (c) 2013, Esoteric Software
- * All rights reserved.
- *
- * You are granted a perpetual, non-exclusive, non-sublicensable and
- * non-transferable license to install, execute and perform the Spine Runtimes
- * Software (the "Software") solely for internal use. Without the written
- * permission of Esoteric Software, you may not (a) modify, translate, adapt or
- * otherwise create derivative works, improvements of the Software or develop
- * new applications using the Software 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 SOFTARE BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 (const char* name) {
- spAtlasPage* self = NEW(spAtlasPage);
- 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(*str->begin) && str->begin < str->end)
- (str->begin)++;
- if (str->begin == str->end) return;
- str->end--;
- while (isspace(*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) {
- static const char* nextStart;
- if (begin) {
- nextStart = begin;
- return 1;
- }
- if (nextStart == end) return 0;
- str->begin = nextStart;
- /* Find next delimiter. */
- while (nextStart != end && *nextStart != '\n')
- nextStart++;
- str->end = nextStart;
- trim(str);
- if (nextStart != end) nextStart++;
- 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* end, Str* str) {
- readLine(0, end, str);
- if (!beginPast(str, ':')) return 0;
- trim(str);
- return 1;
- }
- /* Returns the number of tuple values read (2, 4, or 0 for failure). */
- static int readTuple (const char* end, Str tuple[]) {
- int i;
- Str str;
- readLine(0, end, &str);
- if (!beginPast(&str, ':')) return 0;
- for (i = 0; i < 3; ++i) {
- tuple[i].begin = str.begin;
- if (!beginPast(&str, ',')) {
- if (i == 0) return 0;
- 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 -1;
- }
- static int equals (Str* str, const char* other) {
- return strncmp(other, str->begin, str->end - str->begin) == 0;
- }
- static int toInt (Str* str) {
- return 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_readAtlas (const char* begin, int length, const char* dir) {
- int count;
- const char* end = begin + length;
- int dirLength = (int)strlen(dir);
- int needsSlash = dirLength > 0 && dir[dirLength - 1] != '/' && dir[dirLength - 1] != '\\';
- spAtlas* self = NEW(spAtlas);
- spAtlasPage *page = 0;
- spAtlasPage *lastPage = 0;
- spAtlasRegion *lastRegion = 0;
- Str str;
- Str tuple[4];
- readLine(begin, 0, 0);
- while (readLine(0, 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(name);
- FREE(name);
- if (lastPage)
- lastPage->next = page;
- else
- self->pages = page;
- lastPage = page;
- if (!readValue(end, &str)) return abortAtlas(self);
- page->format = (spAtlasFormat)indexOf(formatNames, 7, &str);
- if (!readTuple(end, tuple)) return abortAtlas(self);
- page->minFilter = (spAtlasFilter)indexOf(textureFilterNames, 7, tuple);
- page->magFilter = (spAtlasFilter)indexOf(textureFilterNames, 7, tuple + 1);
- if (!readValue(end, &str)) return abortAtlas(self);
- if (!equals(&str, "none")) {
- page->uWrap = *str.begin == 'x' ? ATLAS_REPEAT : (*str.begin == 'y' ? ATLAS_CLAMPTOEDGE : ATLAS_REPEAT);
- page->vWrap = *str.begin == 'x' ? ATLAS_CLAMPTOEDGE : (*str.begin == 'y' ? ATLAS_REPEAT : 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(end, &str)) return abortAtlas(self);
- region->rotate = equals(&str, "true");
- if (readTuple(end, tuple) != 2) return abortAtlas(self);
- region->x = toInt(tuple);
- region->y = toInt(tuple + 1);
- if (readTuple(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(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(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(end, tuple)) return abortAtlas(self);
- }
- }
- region->originalWidth = toInt(tuple);
- region->originalHeight = toInt(tuple + 1);
- readTuple(end, tuple);
- region->offsetX = toInt(tuple);
- region->offsetY = toInt(tuple + 1);
- if (!readValue(end, &str)) return abortAtlas(self);
- region->index = toInt(&str);
- }
- }
- return self;
- }
- spAtlas* spAtlas_readAtlasFile (const char* path) {
- 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 = 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_readAtlas(data, length, dir);
- 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;
- }
|