/****************************************************************************** * Spine Runtimes License Agreement * Last updated January 1, 2020. Replaces all prior versions. * * Copyright (c) 2013-2020, Esoteric Software LLC * * Integration of the Spine Runtimes into software or otherwise creating * derivative works of the Spine Runtimes is permitted under the terms and * conditions of Section 2 of the Spine Editor License Agreement: * http://esotericsoftware.com/spine-editor-license * * Otherwise, it is permitted to integrate the Spine Runtimes into software * or otherwise create derivative works of the Spine Runtimes (collectively, * "Products"), provided that each user of the Products must obtain their own * Spine Editor license and redistribution of the Products in any form must * include this license and copyright notice. * * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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 * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #ifdef SPINE_UE4 #include "SpinePluginPrivatePCH.h" #endif #include #include #include #include using namespace spine; Atlas::Atlas(const String &path, TextureLoader *textureLoader, bool createTexture) : _textureLoader(textureLoader) { int dirLength; char *dir; int length; const char *data; /* Get directory from atlas path. */ const char *lastForwardSlash = strrchr(path.buffer(), '/'); const char *lastBackwardSlash = strrchr(path.buffer(), '\\'); const char *lastSlash = lastForwardSlash > lastBackwardSlash ? lastForwardSlash : lastBackwardSlash; if (lastSlash == path) lastSlash++; /* Never drop starting slash. */ dirLength = (int) (lastSlash ? lastSlash - path.buffer() : 0); dir = SpineExtension::calloc(dirLength + 1, __FILE__, __LINE__); memcpy(dir, path.buffer(), dirLength); dir[dirLength] = '\0'; data = SpineExtension::readFile(path, &length); if (data) { load(data, length, dir, createTexture); } SpineExtension::free(data, __FILE__, __LINE__); SpineExtension::free(dir, __FILE__, __LINE__); } Atlas::Atlas(const char *data, int length, const char *dir, TextureLoader *textureLoader, bool createTexture) : _textureLoader( textureLoader) { load(data, length, dir, createTexture); } Atlas::~Atlas() { if (_textureLoader) { for (size_t i = 0, n = _pages.size(); i < n; ++i) { _textureLoader->unload(_pages[i]->getRendererObject()); } } ContainerUtil::cleanUpVectorOfPointers(_pages); ContainerUtil::cleanUpVectorOfPointers(_regions); } void Atlas::flipV() { for (size_t i = 0, n = _regions.size(); i < n; ++i) { AtlasRegion *regionP = _regions[i]; AtlasRegion ®ion = *regionP; region.v = 1 - region.v; region.v2 = 1 - region.v2; } } AtlasRegion *Atlas::findRegion(const String &name) { for (size_t i = 0, n = _regions.size(); i < n; ++i) if (_regions[i]->name == name) return _regions[i]; return NULL; } Vector &Atlas::getPages() { return _pages; } void Atlas::load(const char *begin, int length, const char *dir, bool createTexture) { static const char *formatNames[] = {"", "Alpha", "Intensity", "LuminanceAlpha", "RGB565", "RGBA4444", "RGB888", "RGBA8888"}; static const char *textureFilterNames[] = {"", "Nearest", "Linear", "MipMap", "MipMapNearestNearest", "MipMapLinearNearest", "MipMapNearestLinear", "MipMapLinearLinear"}; int count; const char *end = begin + length; int dirLength = (int) strlen(dir); int needsSlash = dirLength > 0 && dir[dirLength - 1] != '/' && dir[dirLength - 1] != '\\'; AtlasPage *page = NULL; Str str; Str tuple[4]; while (readLine(&begin, end, &str)) { if (str.end - str.begin == 0) { page = 0; } else if (!page) { char *name = mallocString(&str); char *path = SpineExtension::calloc(dirLength + needsSlash + strlen(name) + 1, __FILE__, __LINE__); memcpy(path, dir, dirLength); if (needsSlash) path[dirLength] = '/'; strcpy(path + dirLength + needsSlash, name); page = new(__FILE__, __LINE__) AtlasPage(String(name, true)); int tupleVal = readTuple(&begin, end, tuple); assert(tupleVal == 2); /* size is only optional for an atlas packed with an old TexturePacker. */ page->width = toInt(tuple); page->height = toInt(tuple + 1); readTuple(&begin, end, tuple); page->format = (Format) indexOf(formatNames, 8, tuple); readTuple(&begin, end, tuple); page->minFilter = (TextureFilter) indexOf(textureFilterNames, 8, tuple); page->magFilter = (TextureFilter) indexOf(textureFilterNames, 8, tuple + 1); readValue(&begin, end, &str); page->uWrap = TextureWrap_ClampToEdge; page->vWrap = TextureWrap_ClampToEdge; if (!equals(&str, "none")) { if (str.end - str.begin == 1) { if (*str.begin == 'x') { page->uWrap = TextureWrap_Repeat; } else if (*str.begin == 'y') { page->vWrap = TextureWrap_Repeat; } } else if (equals(&str, "xy")) { page->uWrap = TextureWrap_Repeat; page->vWrap = TextureWrap_Repeat; } } if (createTexture) { if (_textureLoader) _textureLoader->load(*page, String(path)); SpineExtension::free(path, __FILE__, __LINE__); } else page->texturePath = String(path, true); _pages.add(page); } else { AtlasRegion *region = new(__FILE__, __LINE__) AtlasRegion(); region->page = page; region->name = String(mallocString(&str), true); readValue(&begin, end, &str); 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; readTuple(&begin, end, tuple); region->x = toInt(tuple); region->y = toInt(tuple + 1); readTuple(&begin, end, tuple); 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); assert(count); if (count == 4) { /* split is optional */ region->splits.setSize(4, 0); 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); assert(count); if (count == 4) { /* pad is optional, but only present with splits */ region->pads.setSize(4, 0); region->pads[0] = toInt(tuple); region->pads[1] = toInt(tuple + 1); region->pads[2] = toInt(tuple + 2); region->pads[3] = toInt(tuple + 3); readTuple(&begin, end, tuple); } } region->originalWidth = toInt(tuple); region->originalHeight = toInt(tuple + 1); readTuple(&begin, end, tuple); region->offsetX = (float)toInt(tuple); region->offsetY = (float)toInt(tuple + 1); readValue(&begin, end, &str); region->index = toInt(&str); _regions.add(region); } } } void Atlas::trim(Str *str) { while (isspace((unsigned char) *str->begin) && str->begin < str->end) (str->begin)++; if (str->begin == str->end) return; str->end--; while (((unsigned char)*str->end == '\r') && str->end >= str->begin) str->end--; str->end++; } int Atlas::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; } int Atlas::beginPast(Str *str, char c) { const char *begin = str->begin; while (true) { char lastSkippedChar = *begin; if (begin == str->end) return 0; begin++; if (lastSkippedChar == c) break; } str->begin = begin; return 1; } int Atlas::readValue(const char **begin, const char *end, Str *str) { readLine(begin, end, str); if (!beginPast(str, ':')) return 0; trim(str); return 1; } int Atlas::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; } char *Atlas::mallocString(Str *str) { int length = (int) (str->end - str->begin); char *string = SpineExtension::calloc(length + 1, __FILE__, __LINE__); memcpy(string, str->begin, length); string[length] = '\0'; return string; } int Atlas::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; } int Atlas::equals(Str *str, const char *other) { return strncmp(other, str->begin, str->end - str->begin) == 0; } int Atlas::toInt(Str *str) { return (int) strtol(str->begin, (char **) &str->end, 10); }