123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 |
- /******************************************************************************
- * 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.
- *****************************************************************************/
- #if (UNITY_5 || UNITY_5_3_OR_NEWER || UNITY_WSA || UNITY_WP8 || UNITY_WP8_1)
- #define IS_UNITY
- #endif
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.IO;
- using System.Reflection;
- #if WINDOWS_STOREAPP
- using System.Threading.Tasks;
- using Windows.Storage;
- #endif
- namespace Spine {
- public class Atlas : IEnumerable<AtlasRegion> {
- readonly List<AtlasPage> pages = new List<AtlasPage>();
- List<AtlasRegion> regions = new List<AtlasRegion>();
- TextureLoader textureLoader;
- #region IEnumerable implementation
- public IEnumerator<AtlasRegion> GetEnumerator () {
- return regions.GetEnumerator();
- }
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () {
- return regions.GetEnumerator();
- }
- #endregion
- public List<AtlasRegion> Regions { get { return regions; } }
- public List<AtlasPage> Pages { get { return pages; } }
- #if !(IS_UNITY)
- #if WINDOWS_STOREAPP
- private async Task ReadFile(string path, TextureLoader textureLoader) {
- var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
- var file = await folder.GetFileAsync(path).AsTask().ConfigureAwait(false);
- using (var reader = new StreamReader(await file.OpenStreamForReadAsync().ConfigureAwait(false))) {
- try {
- Atlas atlas = new Atlas(reader, Path.GetDirectoryName(path), textureLoader);
- this.pages = atlas.pages;
- this.regions = atlas.regions;
- this.textureLoader = atlas.textureLoader;
- } catch (Exception ex) {
- throw new Exception("Error reading atlas file: " + path, ex);
- }
- }
- }
- public Atlas(string path, TextureLoader textureLoader) {
- this.ReadFile(path, textureLoader).Wait();
- }
- #else
- public Atlas (string path, TextureLoader textureLoader) {
- #if WINDOWS_PHONE
- Stream stream = Microsoft.Xna.Framework.TitleContainer.OpenStream(path);
- using (StreamReader reader = new StreamReader(stream)) {
- #else
- using (StreamReader reader = new StreamReader(path)) {
- #endif // WINDOWS_PHONE
- try {
- Atlas atlas = new Atlas(reader, Path.GetDirectoryName(path), textureLoader);
- this.pages = atlas.pages;
- this.regions = atlas.regions;
- this.textureLoader = atlas.textureLoader;
- } catch (Exception ex) {
- throw new Exception("Error reading atlas file: " + path, ex);
- }
- }
- }
- #endif // WINDOWS_STOREAPP
- #endif
- public Atlas (List<AtlasPage> pages, List<AtlasRegion> regions) {
- if (pages == null) throw new ArgumentNullException("pages", "pages cannot be null.");
- if (regions == null) throw new ArgumentNullException("regions", "regions cannot be null.");
- this.pages = pages;
- this.regions = regions;
- this.textureLoader = null;
- }
- public Atlas (TextReader reader, string imagesDir, TextureLoader textureLoader) {
- if (reader == null) throw new ArgumentNullException("reader", "reader cannot be null.");
- if (imagesDir == null) throw new ArgumentNullException("imagesDir", "imagesDir cannot be null.");
- if (textureLoader == null) throw new ArgumentNullException("textureLoader", "textureLoader cannot be null.");
- this.textureLoader = textureLoader;
- string[] entry = new string[5];
- AtlasPage page = null;
- AtlasRegion region = null;
- var pageFields = new Dictionary<string, Action>(5);
- pageFields.Add("size", () => {
- page.width = int.Parse(entry[1], CultureInfo.InvariantCulture);
- page.height = int.Parse(entry[2], CultureInfo.InvariantCulture);
- });
- pageFields.Add("format", () => {
- page.format = (Format)Enum.Parse(typeof(Format), entry[1], false);
- });
- pageFields.Add("filter", () => {
- page.minFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), entry[1], false);
- page.magFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), entry[2], false);
- });
- pageFields.Add("repeat", () => {
- if (entry[1].IndexOf('x') != -1) page.uWrap = TextureWrap.Repeat;
- if (entry[1].IndexOf('y') != -1) page.vWrap = TextureWrap.Repeat;
- });
- pageFields.Add("pma", () => {
- page.pma = entry[1] == "true";
- });
- var regionFields = new Dictionary<string, Action>(8);
- regionFields.Add("xy", () => { // Deprecated, use bounds.
- region.x = int.Parse(entry[1], CultureInfo.InvariantCulture);
- region.y = int.Parse(entry[2], CultureInfo.InvariantCulture);
- });
- regionFields.Add("size", () => { // Deprecated, use bounds.
- region.width = int.Parse(entry[1], CultureInfo.InvariantCulture);
- region.height = int.Parse(entry[2], CultureInfo.InvariantCulture);
- });
- regionFields.Add("bounds", () => {
- region.x = int.Parse(entry[1], CultureInfo.InvariantCulture);
- region.y = int.Parse(entry[2], CultureInfo.InvariantCulture);
- region.width = int.Parse(entry[3], CultureInfo.InvariantCulture);
- region.height = int.Parse(entry[4], CultureInfo.InvariantCulture);
- });
- regionFields.Add("offset", () => { // Deprecated, use offsets.
- region.offsetX = int.Parse(entry[1], CultureInfo.InvariantCulture);
- region.offsetY = int.Parse(entry[2], CultureInfo.InvariantCulture);
- });
- regionFields.Add("orig", () => { // Deprecated, use offsets.
- region.originalWidth = int.Parse(entry[1], CultureInfo.InvariantCulture);
- region.originalHeight = int.Parse(entry[2], CultureInfo.InvariantCulture);
- });
- regionFields.Add("offsets", () => {
- region.offsetX = int.Parse(entry[1], CultureInfo.InvariantCulture);
- region.offsetY = int.Parse(entry[2], CultureInfo.InvariantCulture);
- region.originalWidth = int.Parse(entry[3], CultureInfo.InvariantCulture);
- region.originalHeight = int.Parse(entry[4], CultureInfo.InvariantCulture);
- });
- regionFields.Add("rotate", () => {
- string value = entry[1];
- if (value == "true")
- region.degrees = 90;
- else if (value != "false")
- region.degrees = int.Parse(value, CultureInfo.InvariantCulture);
- });
- regionFields.Add("index", () => {
- region.index = int.Parse(entry[1], CultureInfo.InvariantCulture);
- });
- string line = reader.ReadLine();
- // Ignore empty lines before first entry.
- while (line != null && line.Trim().Length == 0)
- line = reader.ReadLine();
- // Header entries.
- while (true) {
- if (line == null || line.Trim().Length == 0) break;
- if (ReadEntry(entry, line) == 0) break; // Silently ignore all header fields.
- line = reader.ReadLine();
- }
- // Page and region entries.
- List<string> names = null;
- List<int[]> values = null;
- while (true) {
- if (line == null) break;
- if (line.Trim().Length == 0) {
- page = null;
- line = reader.ReadLine();
- } else if (page == null) {
- page = new AtlasPage();
- page.name = line.Trim();
- while (true) {
- if (ReadEntry(entry, line = reader.ReadLine()) == 0) break;
- Action field;
- if (pageFields.TryGetValue(entry[0], out field)) field(); // Silently ignore unknown page fields.
- }
- textureLoader.Load(page, Path.Combine(imagesDir, page.name));
- pages.Add(page);
- } else {
- region = new AtlasRegion();
- region.page = page;
- region.name = line;
- while (true) {
- int count = ReadEntry(entry, line = reader.ReadLine());
- if (count == 0) break;
- Action field;
- if (regionFields.TryGetValue(entry[0], out field))
- field();
- else {
- if (names == null) {
- names = new List<string>(8);
- values = new List<int[]>(8);
- }
- names.Add(entry[0]);
- int[] entryValues = new int[count];
- for (int i = 0; i < count; i++)
- int.TryParse(entry[i + 1], NumberStyles.Any, CultureInfo.InvariantCulture, out entryValues[i]); // Silently ignore non-integer values.
- values.Add(entryValues);
- }
- }
- if (region.originalWidth == 0 && region.originalHeight == 0) {
- region.originalWidth = region.width;
- region.originalHeight = region.height;
- }
- if (names != null && names.Count > 0) {
- region.names = names.ToArray();
- region.values = values.ToArray();
- names.Clear();
- values.Clear();
- }
- region.u = region.x / (float)page.width;
- region.v = region.y / (float)page.height;
- if (region.degrees == 90) {
- 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;
- }
- regions.Add(region);
- }
- }
- }
- static private int ReadEntry (string[] entry, string line) {
- if (line == null) return 0;
- line = line.Trim();
- if (line.Length == 0) return 0;
- int colon = line.IndexOf(':');
- if (colon == -1) return 0;
- entry[0] = line.Substring(0, colon).Trim();
- for (int i = 1, lastMatch = colon + 1; ; i++) {
- int comma = line.IndexOf(',', lastMatch);
- if (comma == -1) {
- entry[i] = line.Substring(lastMatch).Trim();
- return i;
- }
- entry[i] = line.Substring(lastMatch, comma - lastMatch).Trim();
- lastMatch = comma + 1;
- if (i == 4) return 4;
- }
- }
- public void FlipV () {
- for (int i = 0, n = regions.Count; i < n; i++) {
- AtlasRegion region = regions[i];
- region.v = 1 - region.v;
- region.v2 = 1 - region.v2;
- }
- }
- /// <summary>Returns the first region found with the specified name. This method uses string comparison to find the region, so the result
- /// should be cached rather than calling this method multiple times.</summary>
- /// <returns>The region, or null.</returns>
- public AtlasRegion FindRegion (string name) {
- for (int i = 0, n = regions.Count; i < n; i++)
- if (regions[i].name == name) return regions[i];
- return null;
- }
- public void Dispose () {
- if (textureLoader == null) return;
- for (int i = 0, n = pages.Count; i < n; i++)
- textureLoader.Unload(pages[i].rendererObject);
- }
- }
- public enum Format {
- Alpha,
- Intensity,
- LuminanceAlpha,
- RGB565,
- RGBA4444,
- RGB888,
- RGBA8888
- }
- public enum TextureFilter {
- Nearest,
- Linear,
- MipMap,
- MipMapNearestNearest,
- MipMapLinearNearest,
- MipMapNearestLinear,
- MipMapLinearLinear
- }
- public enum TextureWrap {
- MirroredRepeat,
- ClampToEdge,
- Repeat
- }
- public class AtlasPage {
- public string name;
- public int width, height;
- public Format format = Format.RGBA8888;
- public TextureFilter minFilter = TextureFilter.Nearest;
- public TextureFilter magFilter = TextureFilter.Nearest;
- public TextureWrap uWrap = TextureWrap.ClampToEdge;
- public TextureWrap vWrap = TextureWrap.ClampToEdge;
- public bool pma;
- public object rendererObject;
- public AtlasPage Clone () {
- return MemberwiseClone() as AtlasPage;
- }
- }
- public class AtlasRegion {
- public AtlasPage page;
- public string name;
- public int x, y, width, height;
- public float u, v, u2, v2;
- public float offsetX, offsetY;
- public int originalWidth, originalHeight;
- public int degrees;
- public bool rotate;
- public int index;
- public string[] names;
- public int[][] values;
- public AtlasRegion Clone () {
- return MemberwiseClone() as AtlasRegion;
- }
- }
- public interface TextureLoader {
- void Load (AtlasPage page, string path);
- void Unload (Object texture);
- }
- }
|