mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-06 23:34:53 +08:00
271 lines
7.8 KiB
Lua
271 lines
7.8 KiB
Lua
-------------------------------------------------------------------------------
|
|
-- 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.
|
|
-------------------------------------------------------------------------------
|
|
|
|
local setmetatable = setmetatable
|
|
local tonumber = tonumber
|
|
local table_insert = table.insert
|
|
local math_abs = math.abs
|
|
|
|
local TextureAtlasRegion = require "spine-lua.TextureAtlasRegion"
|
|
local TextureWrap = require "spine-lua.TextureWrap"
|
|
local TextureFilter = require "spine-lua.TextureFilter"
|
|
|
|
local TextureAtlasPage = {}
|
|
TextureAtlasPage.__index = TextureAtlasPage
|
|
|
|
function TextureAtlasPage.new ()
|
|
local self = {
|
|
name = nil,
|
|
minFilter = nil,
|
|
magFilter = nil,
|
|
uWrap = nil,
|
|
vWrap = nil,
|
|
texture = nil,
|
|
width = 0,
|
|
height = 0,
|
|
pma = false
|
|
}
|
|
setmetatable(self, TextureAtlasPage)
|
|
return self
|
|
end
|
|
|
|
|
|
local TextureAtlas = {}
|
|
TextureAtlas.__index = TextureAtlas
|
|
|
|
function TextureAtlas.new (atlasContent, imageLoader)
|
|
local self = {
|
|
pages = {},
|
|
regions = {}
|
|
}
|
|
setmetatable(self, TextureAtlas)
|
|
|
|
self:parse(atlasContent, imageLoader)
|
|
|
|
return self
|
|
end
|
|
|
|
function TextureAtlas:parse (atlasContent, imageLoader)
|
|
if not atlasContent then error("atlasContent cannot be nil.", 2) end
|
|
if not imageLoader then error("imageLoader cannot be nil.", 2) end
|
|
|
|
local readLine = atlasContent:gmatch("[ \t]*(.-)[ \t]*\r?\n")
|
|
|
|
local trim = function (value)
|
|
return value:match("^%s*(.-)%s*$")
|
|
end
|
|
|
|
local entry = {}
|
|
local readEntry = function (entry, line)
|
|
if not line then return 0 end
|
|
if line:len() == 0 then return 0 end
|
|
|
|
local colon = line:find(":")
|
|
if not colon then return 0 end
|
|
entry[0] = trim(line:sub(1, colon - 1))
|
|
local lastMatch = colon + 1
|
|
local i = 1
|
|
while true do
|
|
local comma = line:find(",", lastMatch)
|
|
if not comma then
|
|
entry[i] = trim(line:sub(lastMatch))
|
|
return i
|
|
end
|
|
entry[i] = trim(line:sub(lastMatch, comma - 1))
|
|
lastMatch = comma + 1
|
|
if i == 4 then return 4 end
|
|
i = i + 1
|
|
end
|
|
end
|
|
|
|
local page
|
|
local region
|
|
|
|
local pageFields = {}
|
|
pageFields["size"] = function ()
|
|
page.width = tonumber(entry[1])
|
|
page.height = tonumber(entry[2])
|
|
end
|
|
pageFields["format"] = function ()
|
|
-- page.format = Format[tuple[0]] we don't need format in Lua
|
|
end
|
|
pageFields["filter"] = function ()
|
|
page.minFilter = TextureFilter[entry[1]]
|
|
page.magFilter = TextureFilter[entry[2]]
|
|
end
|
|
pageFields["repeat"] = function ()
|
|
if entry[1]:find("x") then page.uWrap = TextureWrap.Repeat end
|
|
if entry[1]:find("y") then page.vWrap = TextureWrap.Repeat end
|
|
end
|
|
pageFields["pma"] = function ()
|
|
page.pma = entry[1] == "true"
|
|
end
|
|
|
|
local regionFields = {}
|
|
regionFields["xy"] = function () -- Deprecated, use bounds.
|
|
region.x = tonumber(entry[1])
|
|
region.y = tonumber(entry[2])
|
|
end
|
|
regionFields["size"] = function () -- Deprecated, use bounds.
|
|
region.width = tonumber(entry[1])
|
|
region.height = tonumber(entry[2])
|
|
end
|
|
regionFields["bounds"] = function ()
|
|
region.x = tonumber(entry[1])
|
|
region.y = tonumber(entry[2])
|
|
region.width = tonumber(entry[3])
|
|
region.height = tonumber(entry[4])
|
|
end
|
|
regionFields["offset"] = function () -- Deprecated, use offsets.
|
|
region.offsetX = tonumber(entry[1])
|
|
region.offsetY = tonumber(entry[2])
|
|
end
|
|
regionFields["orig"] = function () -- Deprecated, use offsets.
|
|
region.originalWidth = tonumber(entry[1])
|
|
region.originalHeight = tonumber(entry[2])
|
|
end
|
|
regionFields["offsets"] = function ()
|
|
region.offsetX = tonumber(entry[1])
|
|
region.offsetY = tonumber(entry[2])
|
|
region.originalWidth = tonumber(entry[3])
|
|
region.originalHeight = tonumber(entry[4])
|
|
end
|
|
regionFields["rotate"] = function ()
|
|
local value = entry[1]
|
|
if value == "true" then
|
|
region.degrees = 90
|
|
elseif value ~= "false" then
|
|
region.degrees = tonumber(value)
|
|
end
|
|
end
|
|
regionFields["index"] = function ()
|
|
region.index = tonumber(entry[1])
|
|
end
|
|
|
|
local line = readLine()
|
|
-- Ignore empty lines before first entry.
|
|
while line and line:len() == 0 do
|
|
line = readLine()
|
|
end
|
|
-- Header entries.
|
|
while true do
|
|
if not line or line:len() == 0 then break end
|
|
if readEntry(entry, line) == 0 then break end -- Silently ignore all header fields.
|
|
line = readLine()
|
|
end
|
|
|
|
-- Page and region entries.
|
|
local names
|
|
local values
|
|
while true do
|
|
if not line then break end
|
|
if line:len() == 0 then
|
|
page = nil
|
|
line = readLine()
|
|
elseif not page then
|
|
page = TextureAtlasPage.new()
|
|
page.name = trim(line)
|
|
while true do
|
|
line = readLine()
|
|
if readEntry(entry, line) == 0 then break end
|
|
local field = pageFields[entry[0]]
|
|
if field then field() end
|
|
end
|
|
page.texture = imageLoader(page.name)
|
|
-- FIXME - Apply the filter and wrap settings to the texture.
|
|
-- page.texture:setFilters(page.minFilter, page.magFilter)
|
|
-- page.texture:setWraps(page.uWrap, page.vWrap)
|
|
table_insert(self.pages, page)
|
|
else
|
|
region = TextureAtlasRegion.new()
|
|
region.page = page
|
|
region.name = line
|
|
while true do
|
|
line = readLine()
|
|
local count = readEntry(entry, line)
|
|
if count == 0 then break end
|
|
local field = regionFields[entry[0]]
|
|
if field then
|
|
field()
|
|
else
|
|
if not names then
|
|
names = {}
|
|
values = {}
|
|
end
|
|
table_insert(names, entry[0])
|
|
local entryValues = {}
|
|
local i = 0
|
|
while i < count do
|
|
table_insert(entryValues, tonumber(entry[i + 1]))
|
|
i = i + 1
|
|
end
|
|
table_insert(values, entryValues)
|
|
end
|
|
end
|
|
if region.originalWidth == 0 and region.originalHeight == 0 then
|
|
region.originalWidth = region.width
|
|
region.originalHeight = region.height
|
|
end
|
|
if names and #names > 0 then
|
|
region.names = names
|
|
region.values = values
|
|
names = nil
|
|
values = nil
|
|
end
|
|
region.u = region.x / page.width
|
|
region.v = region.y / page.height
|
|
if region.degrees == 90 then
|
|
region.u2 = (region.x + region.height) / page.width
|
|
region.v2 = (region.y + region.width) / page.height
|
|
else
|
|
region.u2 = (region.x + region.width) / page.width
|
|
region.v2 = (region.y + region.height) / page.height
|
|
end
|
|
region.texture = page.texture
|
|
table_insert(self.regions, region)
|
|
end
|
|
end
|
|
end
|
|
|
|
function TextureAtlas:findRegion(name)
|
|
for _, region in ipairs(self.regions) do
|
|
if region.name == name then return region end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
function TextureAtlas:dispose()
|
|
for _, page in ipairs(self.pairs) do
|
|
-- FIXME implement disposing of pages
|
|
-- love2d doesn't support manual disposing
|
|
end
|
|
end
|
|
|
|
return TextureAtlas
|