Atlas.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /******************************************************************************
  2. * Spine Runtimes Software License
  3. * Version 2
  4. *
  5. * Copyright (c) 2013, Esoteric Software
  6. * All rights reserved.
  7. *
  8. * You are granted a perpetual, non-exclusive, non-sublicensable and
  9. * non-transferable license to install, execute and perform the Spine Runtimes
  10. * Software (the "Software") solely for internal use. Without the written
  11. * permission of Esoteric Software, you may not (a) modify, translate, adapt or
  12. * otherwise create derivative works, improvements of the Software or develop
  13. * new applications using the Software or (b) remove, delete, alter or obscure
  14. * any trademarks or any copyright, trademark, patent or other intellectual
  15. * property or proprietary rights notices on or in the Software, including
  16. * any copy thereof. Redistributions in binary or source form must include
  17. * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
  18. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  19. * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  20. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
  21. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  22. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  23. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  24. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. *****************************************************************************/
  28. #include <spine/Atlas.h>
  29. #include <ctype.h>
  30. #include <spine/extension.h>
  31. spAtlasPage* spAtlasPage_create (const char* name) {
  32. spAtlasPage* self = NEW(spAtlasPage);
  33. MALLOC_STR(self->name, name);
  34. return self;
  35. }
  36. void spAtlasPage_dispose (spAtlasPage* self) {
  37. _spAtlasPage_disposeTexture(self);
  38. FREE(self->name);
  39. FREE(self);
  40. }
  41. /**/
  42. spAtlasRegion* spAtlasRegion_create () {
  43. return NEW(spAtlasRegion) ;
  44. }
  45. void spAtlasRegion_dispose (spAtlasRegion* self) {
  46. FREE(self->name);
  47. FREE(self->splits);
  48. FREE(self->pads);
  49. FREE(self);
  50. }
  51. /**/
  52. typedef struct {
  53. const char* begin;
  54. const char* end;
  55. } Str;
  56. static void trim (Str* str) {
  57. while (isspace(*str->begin) && str->begin < str->end)
  58. (str->begin)++;
  59. if (str->begin == str->end) return;
  60. str->end--;
  61. while (isspace(*str->end) && str->end >= str->begin)
  62. str->end--;
  63. str->end++;
  64. }
  65. /* Tokenize string without modification. Returns 0 on failure. */
  66. static int readLine (const char* begin, const char* end, Str* str) {
  67. static const char* nextStart;
  68. if (begin) {
  69. nextStart = begin;
  70. return 1;
  71. }
  72. if (nextStart == end) return 0;
  73. str->begin = nextStart;
  74. /* Find next delimiter. */
  75. while (nextStart != end && *nextStart != '\n')
  76. nextStart++;
  77. str->end = nextStart;
  78. trim(str);
  79. if (nextStart != end) nextStart++;
  80. return 1;
  81. }
  82. /* Moves str->begin past the first occurence of c. Returns 0 on failure. */
  83. static int beginPast (Str* str, char c) {
  84. const char* begin = str->begin;
  85. while (1) {
  86. char lastSkippedChar = *begin;
  87. if (begin == str->end) return 0;
  88. begin++;
  89. if (lastSkippedChar == c) break;
  90. }
  91. str->begin = begin;
  92. return 1;
  93. }
  94. /* Returns 0 on failure. */
  95. static int readValue (const char* end, Str* str) {
  96. readLine(0, end, str);
  97. if (!beginPast(str, ':')) return 0;
  98. trim(str);
  99. return 1;
  100. }
  101. /* Returns the number of tuple values read (2, 4, or 0 for failure). */
  102. static int readTuple (const char* end, Str tuple[]) {
  103. int i;
  104. Str str;
  105. readLine(0, end, &str);
  106. if (!beginPast(&str, ':')) return 0;
  107. for (i = 0; i < 3; ++i) {
  108. tuple[i].begin = str.begin;
  109. if (!beginPast(&str, ',')) {
  110. if (i == 0) return 0;
  111. break;
  112. }
  113. tuple[i].end = str.begin - 2;
  114. trim(&tuple[i]);
  115. }
  116. tuple[i].begin = str.begin;
  117. tuple[i].end = str.end;
  118. trim(&tuple[i]);
  119. return i + 1;
  120. }
  121. static char* mallocString (Str* str) {
  122. int length = (int)(str->end - str->begin);
  123. char* string = MALLOC(char, length + 1);
  124. memcpy(string, str->begin, length);
  125. string[length] = '\0';
  126. return string;
  127. }
  128. static int indexOf (const char** array, int count, Str* str) {
  129. int length = (int)(str->end - str->begin);
  130. int i;
  131. for (i = count - 1; i >= 0; i--)
  132. if (strncmp(array[i], str->begin, length) == 0) return i;
  133. return -1;
  134. }
  135. static int equals (Str* str, const char* other) {
  136. return strncmp(other, str->begin, str->end - str->begin) == 0;
  137. }
  138. static int toInt (Str* str) {
  139. return strtol(str->begin, (char**)&str->end, 10);
  140. }
  141. static spAtlas* abortAtlas (spAtlas* self) {
  142. spAtlas_dispose(self);
  143. return 0;
  144. }
  145. static const char* formatNames[] = {"Alpha", "Intensity", "LuminanceAlpha", "RGB565", "RGBA4444", "RGB888", "RGBA8888"};
  146. static const char* textureFilterNames[] = {"Nearest", "Linear", "MipMap", "MipMapNearestNearest", "MipMapLinearNearest",
  147. "MipMapNearestLinear", "MipMapLinearLinear"};
  148. spAtlas* spAtlas_readAtlas (const char* begin, int length, const char* dir) {
  149. int count;
  150. const char* end = begin + length;
  151. int dirLength = (int)strlen(dir);
  152. int needsSlash = dirLength > 0 && dir[dirLength - 1] != '/' && dir[dirLength - 1] != '\\';
  153. spAtlas* self = NEW(spAtlas);
  154. spAtlasPage *page = 0;
  155. spAtlasPage *lastPage = 0;
  156. spAtlasRegion *lastRegion = 0;
  157. Str str;
  158. Str tuple[4];
  159. readLine(begin, 0, 0);
  160. while (readLine(0, end, &str)) {
  161. if (str.end - str.begin == 0) {
  162. page = 0;
  163. } else if (!page) {
  164. char* name = mallocString(&str);
  165. char* path = MALLOC(char, dirLength + needsSlash + strlen(name) + 1);
  166. memcpy(path, dir, dirLength);
  167. if (needsSlash) path[dirLength] = '/';
  168. strcpy(path + dirLength + needsSlash, name);
  169. page = spAtlasPage_create(name);
  170. FREE(name);
  171. if (lastPage)
  172. lastPage->next = page;
  173. else
  174. self->pages = page;
  175. lastPage = page;
  176. if (!readValue(end, &str)) return abortAtlas(self);
  177. page->format = (spAtlasFormat)indexOf(formatNames, 7, &str);
  178. if (!readTuple(end, tuple)) return abortAtlas(self);
  179. page->minFilter = (spAtlasFilter)indexOf(textureFilterNames, 7, tuple);
  180. page->magFilter = (spAtlasFilter)indexOf(textureFilterNames, 7, tuple + 1);
  181. if (!readValue(end, &str)) return abortAtlas(self);
  182. if (!equals(&str, "none")) {
  183. page->uWrap = *str.begin == 'x' ? ATLAS_REPEAT : (*str.begin == 'y' ? ATLAS_CLAMPTOEDGE : ATLAS_REPEAT);
  184. page->vWrap = *str.begin == 'x' ? ATLAS_CLAMPTOEDGE : (*str.begin == 'y' ? ATLAS_REPEAT : ATLAS_REPEAT);
  185. }
  186. _spAtlasPage_createTexture(page, path);
  187. FREE(path);
  188. } else {
  189. spAtlasRegion *region = spAtlasRegion_create();
  190. if (lastRegion)
  191. lastRegion->next = region;
  192. else
  193. self->regions = region;
  194. lastRegion = region;
  195. region->page = page;
  196. region->name = mallocString(&str);
  197. if (!readValue(end, &str)) return abortAtlas(self);
  198. region->rotate = equals(&str, "true");
  199. if (readTuple(end, tuple) != 2) return abortAtlas(self);
  200. region->x = toInt(tuple);
  201. region->y = toInt(tuple + 1);
  202. if (readTuple(end, tuple) != 2) return abortAtlas(self);
  203. region->width = toInt(tuple);
  204. region->height = toInt(tuple + 1);
  205. region->u = region->x / (float)page->width;
  206. region->v = region->y / (float)page->height;
  207. if (region->rotate) {
  208. region->u2 = (region->x + region->height) / (float)page->width;
  209. region->v2 = (region->y + region->width) / (float)page->height;
  210. } else {
  211. region->u2 = (region->x + region->width) / (float)page->width;
  212. region->v2 = (region->y + region->height) / (float)page->height;
  213. }
  214. if (!(count = readTuple(end, tuple))) return abortAtlas(self);
  215. if (count == 4) { /* split is optional */
  216. region->splits = MALLOC(int, 4);
  217. region->splits[0] = toInt(tuple);
  218. region->splits[1] = toInt(tuple + 1);
  219. region->splits[2] = toInt(tuple + 2);
  220. region->splits[3] = toInt(tuple + 3);
  221. if (!(count = readTuple(end, tuple))) return abortAtlas(self);
  222. if (count == 4) { /* pad is optional, but only present with splits */
  223. region->pads = MALLOC(int, 4);
  224. region->pads[0] = toInt(tuple);
  225. region->pads[1] = toInt(tuple + 1);
  226. region->pads[2] = toInt(tuple + 2);
  227. region->pads[3] = toInt(tuple + 3);
  228. if (!readTuple(end, tuple)) return abortAtlas(self);
  229. }
  230. }
  231. region->originalWidth = toInt(tuple);
  232. region->originalHeight = toInt(tuple + 1);
  233. readTuple(end, tuple);
  234. region->offsetX = toInt(tuple);
  235. region->offsetY = toInt(tuple + 1);
  236. if (!readValue(end, &str)) return abortAtlas(self);
  237. region->index = toInt(&str);
  238. }
  239. }
  240. return self;
  241. }
  242. spAtlas* spAtlas_readAtlasFile (const char* path) {
  243. int dirLength;
  244. char *dir;
  245. int length;
  246. const char* data;
  247. spAtlas* atlas = 0;
  248. /* Get directory from atlas path. */
  249. const char* lastForwardSlash = strrchr(path, '/');
  250. const char* lastBackwardSlash = strrchr(path, '\\');
  251. const char* lastSlash = lastForwardSlash > lastBackwardSlash ? lastForwardSlash : lastBackwardSlash;
  252. if (lastSlash == path) lastSlash++; /* Never drop starting slash. */
  253. dirLength = lastSlash ? lastSlash - path : 0;
  254. dir = MALLOC(char, dirLength + 1);
  255. memcpy(dir, path, dirLength);
  256. dir[dirLength] = '\0';
  257. data = _spUtil_readFile(path, &length);
  258. if (data) atlas = spAtlas_readAtlas(data, length, dir);
  259. FREE(data);
  260. FREE(dir);
  261. return atlas;
  262. }
  263. void spAtlas_dispose (spAtlas* self) {
  264. spAtlasRegion* region, *nextRegion;
  265. spAtlasPage* page = self->pages;
  266. while (page) {
  267. spAtlasPage* nextPage = page->next;
  268. spAtlasPage_dispose(page);
  269. page = nextPage;
  270. }
  271. region = self->regions;
  272. while (region) {
  273. nextRegion = region->next;
  274. spAtlasRegion_dispose(region);
  275. region = nextRegion;
  276. }
  277. FREE(self);
  278. }
  279. spAtlasRegion* spAtlas_findRegion (const spAtlas* self, const char* name) {
  280. spAtlasRegion* region = self->regions;
  281. while (region) {
  282. if (strcmp(region->name, name) == 0) return region;
  283. region = region->next;
  284. }
  285. return 0;
  286. }