Skip to content

Instantly share code, notes, and snippets.

@Vavius
Created September 27, 2015 07:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Vavius/29f91f19dd85944e77f0 to your computer and use it in GitHub Desktop.
Save Vavius/29f91f19dd85944e77f0 to your computer and use it in GitHub Desktop.
--------------------------------------------------------------------------------
-- 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