Created
September 27, 2015 07:00
-
-
Save Vavius/29f91f19dd85944e77f0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-------------------------------------------------------------------------------- | |
-- DynamicTexturePack.lua | |
-- | |
-- | |
-------------------------------------------------------------------------------- | |
local DynamicTexturePack = class() | |
local Page = class() | |
local MAX_SIZE = MOAIGfxDevice.getMaxTextureSize() or 2048 | |
MAX_SIZE = math.clamp(MAX_SIZE, 1024, 4096) | |
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- | |
function DynamicTexturePack:init(width, height, scale, padding) | |
self.pages = {} | |
self.items = {} | |
self.width = width or MAX_SIZE | |
self.height = height or MAX_SIZE | |
self.scale = scale or 1 | |
self.padding = padding or 0 | |
self:newPage() | |
end | |
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- | |
function DynamicTexturePack:addRect(image, key, w, h) | |
if self.items[key] then | |
return self:getDeckAndIndex(key) | |
end | |
local idx, pageIdx | |
for i, page in ipairs(self.pages) do | |
idx = page:place(image, w, h, self.paintCallback) | |
if idx then | |
pageIdx = i | |
break | |
end | |
end | |
if not idx or not pageIdx then | |
local page = self:newPage() | |
idx = page:place(image, w, h, self.paintCallback) | |
pageIdx = #self.pages | |
end | |
if not idx then | |
log.error("Cannot fit image into cache") | |
end | |
self.items[key] = {page = pageIdx, index = idx} | |
local deck = self.pages[pageIdx].deck | |
return deck, idx | |
end | |
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- | |
function DynamicTexturePack:getDeckAndIndex(key) | |
local item = self.items[key] | |
if not item then | |
return | |
end | |
local deck = self.pages[item.page].deck | |
return deck, item.index | |
end | |
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- | |
function DynamicTexturePack:newPage() | |
local page = Page(self.width, self.height, self.scale, self.padding) | |
table.insert(self.pages, page) | |
return page | |
end | |
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- | |
function DynamicTexturePack:rebuildDeck() | |
for _, page in ipairs(self.pages) do | |
page:rebuildDeck() | |
end | |
end | |
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- | |
function DynamicTexturePack:setPaintCallback(func) | |
self.paintCallback = func | |
end | |
--============================================================================-- | |
-- Page | |
--============================================================================-- | |
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- | |
function Page:findRow(width, height) | |
local w, h = self.width, self.height | |
local best | |
for i, row in ipairs(self.rows) do | |
if (row.height >= height) and (row.x + width <= w) then | |
if not best or (row.height < best.height) then | |
best = row | |
end | |
end | |
end | |
-- try to alloc new row | |
if not best then | |
local last = self.rows[#self.rows] | |
local y = last and (last.y + last.height) or 0 | |
if y > h - height then | |
-- no space left for new row | |
return | |
end | |
best = {x = 0, y = y, height = height} | |
table.insert(self.rows, best) | |
end | |
return best | |
end | |
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- | |
function Page:init(width, height, scale, padding) | |
local image = MOAIImageTexture.new() | |
local deck = MOAIGfxQuadDeck2D.new() | |
deck:setTexture(image) | |
self.width = width | |
self.height = height | |
self.scale = scale or 1 | |
self.padding = padding or 0 | |
image:init(width, height) | |
self.deck = deck | |
self.image = image | |
self.items = {} | |
self.rows = {} | |
end | |
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- | |
function Page:paint(canvas, image, xMin, yMin, xMax, yMax) | |
local w, h = image:getSize() | |
canvas:copyRect(image, 0, 0, w, h, xMin, yMin, xMax, yMax) | |
canvas:updateRegion() | |
end | |
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- | |
function Page:place(image, width, height, paintCallback) | |
local pad = self.padding | |
width = width + 2 * pad | |
height = height + 2 * pad | |
local row = self:findRow(width, height) | |
if not row then return end | |
local x, y = row.x, row.y | |
row.x = x + width | |
local xMin, yMin, xMax, yMax = x + pad, y + pad, x + width - pad, y + height - pad | |
local index = #self.items + 1 | |
if paintCallback then | |
paintCallback(self.image, image, xMin, yMin, xMax, yMax, index) | |
else | |
self:paint(self.image, image, xMin, yMin, xMax, yMax) | |
end | |
table.insert(self.items, {x + pad, y + pad, width - 2 * pad, height - 2 * pad}) | |
return index | |
end | |
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- | |
function Page:rebuildDeck() | |
local width, height = self.width, self.height | |
local deck, items = self.deck, self.items | |
local total = #items | |
deck:reserve(total) | |
for i, item in ipairs(items) do | |
local x, y, w, h = unpack(item) | |
local s = self.scale | |
deck:setRect(i, -0.5 * w / s, -0.5 * h / s, 0.5 * w / s, 0.5 * h / s) | |
deck:setUVRect(i, x / width, (y + h) / height, (x + w) / width, y / height) | |
end | |
end | |
--============================================================================-- | |
-- Sample/test | |
--============================================================================-- | |
-- local DynamicTexturePack = require("util.DynamicTexturePack") | |
-- local texturePack = DynamicTexturePack(2048) | |
-- local path = '../assets/ipad/50/gui' | |
-- local files = MOAIFileSystem.listFiles(path) | |
-- for i, file in pairs(files) do | |
-- if string.endswith(file, '.png') then | |
-- local image = MOAIImage.new() | |
-- image:load(string.pathJoin(path, file)) | |
-- local w, h = image:getSize() | |
-- texturePack:addRect(image, file, w, h) | |
-- end | |
-- end | |
-- for i, p in pairs(texturePack.pages) do | |
-- p.image:writePNG("page" .. i .. ".png") | |
-- end | |
return DynamicTexturePack |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment