{"id":13524937,"url":"https://github.com/EngineerSmith/Runtime-TextureAtlas","last_synced_at":"2025-04-01T04:30:26.118Z","repository":{"id":44716708,"uuid":"416052279","full_name":"EngineerSmith/Runtime-TextureAtlas","owner":"EngineerSmith","description":"Love2D runtime texture atlas; no external tools needed","archived":false,"fork":false,"pushed_at":"2022-06-09T19:20:20.000Z","size":92,"stargazers_count":21,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-25T21:48:36.516Z","etag":null,"topics":["gamedev","graphics","love","love2d","lua","texture-atlas"],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/EngineerSmith.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-10-11T19:01:29.000Z","updated_at":"2025-01-22T12:50:38.000Z","dependencies_parsed_at":"2022-09-11T20:13:01.227Z","dependency_job_id":null,"html_url":"https://github.com/EngineerSmith/Runtime-TextureAtlas","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EngineerSmith%2FRuntime-TextureAtlas","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EngineerSmith%2FRuntime-TextureAtlas/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EngineerSmith%2FRuntime-TextureAtlas/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EngineerSmith%2FRuntime-TextureAtlas/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/EngineerSmith","download_url":"https://codeload.github.com/EngineerSmith/Runtime-TextureAtlas/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246585521,"owners_count":20801022,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gamedev","graphics","love","love2d","lua","texture-atlas"],"created_at":"2024-08-01T06:01:14.764Z","updated_at":"2025-04-01T04:30:25.888Z","avatar_url":"https://github.com/EngineerSmith.png","language":"Lua","readme":"\n\n# Runtime Texture Atlas (RTA)\nA Love2D runtime texture atlas designed to be easy to use and memory optimized. Creates texture atlas when loading than having to use an external tool to create an atlas. Tested and written on **love 11.3 Android** and **love 11.4 Windows**.\n\nThere are two types of Texture atlas you have access to:\n\n**Fixed Size**: *All images must have the same width and height*\n\n  Uses home-brew algorithm to pack images. Estimates size of the canvas with ceil(sqrt(numOfImages)) for the number of columns, then optimizes number of rows. (e.g. ceil(sqrt(50))=8, instead of an 8x8= grid for the atlas, it'll use 8x7 grid to avoid wasting the extra row). If you are baking an awful lot of images, and the window is freezing: see advice to solve this issues below.\n\n**Note, due to algorithm constraints if it cannot get the size it requires it will throw an error.** This is due to the algorithm having no wiggly room to rearrange itself. This will unlikely be an issue unless you're forcing it to fit within a rectangle.\n\n**Dynamic Size**: *All images can be whatever size they want*\n  Uses a home-brew algorithm to pack images together based on cells. Unit tests show it takes 40% longer to bake than fixed size (This is given due to it being more complex than filling a grid). Check out `packing.lua` to see how they are being packed together. \n\n  Originally RTA used binary tree packing which didn't produced good results. This new packing algorithm is not only (almost) twice as fast but much more better at packing images optimally than the binary tree solution.\n ### How to run RTA headless\n If you're looking to run RTA headless, it supports drawing to imageData that is returned from baking. You can find this implemented in the [ETA command line tool](https://github.com/EngineerSmith/Export-TextureAtlas).\n### Advice: Window Freezing during baking\n\n  If your game window is freezing, it is due to the baking process taking too long. Try to avoid baking until the end, and avoid baking over and over unless you need to keep changing the images within the atlas. (If you're doing this to say animate, add all frames and then select the different quads in the draw)\n  \n  Otherwise there is no way easy way around it. You can use another library such as [Lily](https://github.com/MikuAuahDark/lily) to multi-thread load image - this will increase performance a little for loading your images.\n\nTLDR; bake once at the start of your game\n## Examples\n### Fixed Size\nAll images must be the same size\n```lua\nlocal textureAtlas = require(\"libs.TA\")\nlocal ta = textureAtlas.newFixedSize(16, 32)\nta:setFilter(\"nearest\")\n\nta:add(love.graphics.newImage(\"duck.png\"), \"duck\") -- throws error if images aren't 16x32\nta:add(love.graphics.newImage(\"cat.png\"), \"cat\")\nta:add(love.graphics.newImage(\"dog.png\"), \"dog\")\nta:add(love.graphics.newImage(\"rabbit.png\"), \"rabbit\")\n\nta:bake()\n\nta:remove(\"dog\")\nta:remove(\"rabbit\", true) -- Remove rabbit and bake changes\n\nta:hardBake() -- Cannot add or remove images after call, and deletes all references to given images so they can be cleaned from memory\ncollectgarbage(\"collect\")\n\nlocal catDraw = ta:getDrawFuncForID(\"cat\")\n\nlove.draw = function()\n    ta:draw(\"duck\", 50,50)\n    catDraw(100,50, 0, 5,5)\nend\n```\n### Dynamic Size\nImages don't have to be the same size\n```lua\nlocal textureAtlas = require(\"libs.TA\")\nlocal ta = textureAtlas.newDynamicSize()\nta:setFilter(\"nearest\")\n\nta:add(love.graphics.newImage(\"521x753.png\"), \"duck\")\nta:add(love.graphics.newImage(\"25x1250.png\"), \"cat\")\nta:add(love.graphics.newImage(\"duck.png\"), \"duck\") -- Replace previous image at id without having to call ta:remove\nta:add(love.graphic.newImage(\"rabbit.png\", \"rabbit\")\n\nta:bake(\"height\") -- Sorting algorithm optimizes space use\n\nta:remove(\"rabbit\") -- will need to bake again\nta:remove(\"cat\", true, \"area\") -- remove graphic, bake with this sort\n\nta:hardBake() -- Cannot add or remove images, and deletes all references to given images so they can be cleaned from memory\ncollectgarbage(\"collect\")\n\nlocal duckDraw = ta:getDrawFuncForID(\"duck\")\n\nlove.draw = function()\n    ta:draw(\"banner\", 50,50)\n    duckDraw(100,50, 0, 5,5)\n    love.graphics.print((\"x%d:y%d\\nw%d:h%d\"):format(ta:getViewport(\"duck\")))\nend\n```\n## Docs\nClone into your lib/include file for your love2d project.\n\nE.g. `git clone https://github.com/EngineerSmith/Runtime-TextureAtlas libs/TA`\n### require\nRequire the library using the init.lua\n```lua\nlocal textureAtlas = require(\"libs.TA\") -- the location where it has been cloned to\n```\n### textureAtlas.new\nCreate an atlas to add images too:\n\n- Fixed Size atlas require all added images to have the same width and height\n- Dynamic Size atlas allows for any size of image\n\nVariables explained:\n* Padding: allows you to add a border around each image\n* Extrude: allows you to extend the image using the clamp warp mode (or that which you've set for the image) See [here](https://love2d.org/wiki/WrapMode) for an example of clamp.\n* Spacing: allows you to add space between each image, different from padding as it doesn't add space between atlas edge and the images.\nPadding Vs Spacing: 1 pixel of spacing would leave a 1 pixel gap between images, whilst 1 pixel of padding would leave a 2 pixel gap between each image. Spacing would allow the texture to go up to the edges of the atlas, whilst padding will not.\n```lua\nlocal fs = textureAtlas.newFixedSize(width, height = width, padding = 1, extrude = 0, spacing = 0)\nlocal ds = textureAtlas.newDynamicSize(padding = 1, extrude = 0, spacing = 0)\n\ntextureAtlas.newFixedSize(16) -- 16x16 only, padding 1 pixel\ntextureAtlas.newFixedSize(32,64, 5) -- 32x64 only, padding 5 pixel\ntextureAtlas.newFixedSize(64,64, 5, 3) -- 64x64 only, padding 5 pixels, extrude 3 pixels\ntextureAtlas.newFixedSize(64,64, 2, 2, 2) -- 64x64 only, padding 2 pixels, extrude 2 pixels, space 2 pixels\n\ntextureAtlas.newDynamicSize(1) -- padding 1 pixel\ntextureAtlas.newDynamicSize(5) -- padding 5 pixels\ntextureAtlas.newDynamicSize(3, 3) -- padding 3 pixels, extrude 3 pixels\ntextureAtlas.newDynamicSize(2, 2, 2) -- padding 2 pixels, extrude 2 pixels, space 2 pixels\n```\n### textureAtlas:setFilter(min, mag = min)\nSimilar to `image:setFilter`; however, will always override default filter even if not changed. E.g. if `love.graphics.setDefaultFilter(\"nearest\", \"nearest\")` is called, textureAtlas will continue to bake in `\"linear\"`\n```lua\nta:setFilter(min, mag = min)\nta:setFilter(\"nearest\")\n```\n### textureAtlas:useImageData(boolean = false)\nChange mode for the texture atlas to use `ImageData` instead of `love.graphics`. By default if `love.graphics` isn't loaded when `textureAtlas.new` is called, it will use `imageData` mode automatically.\n\n**You cannot change this setting after adding images to the texture atlas!**\n### textureAtlas:setBakeAsPow2(boolean = false)\nWill round the atlas width and height to their nearest power of 2 value. Do note that the packing algorithms are not optimized for spacing towards the closest power of 2, and you may be left with a lot of empty space. This function doesn't re-bake the atlas, and can be called after hard bake function has been ran, but it will not do anything. This function is useful for those who want to export an atlas as an external resource as some old graphics cards and old smartphones requires textures to be power of 2 and can (in some cases) increase performance minimally. \n```lua\nta:setBakeAsPow2(true)\nta:setBakeAsPow2(false)\n```\n### textureAtlas:setPadding(padding)\nSets padding around each image, this will push the image away from the edge of the atlas.\n```lua\nta:setPadding(1)\nta:setPadding(4)\n```\n### textureAtlas:setExtrude(extrude)\nSets how much an image should be extruded by. It allows you to extend the image using the clamp warp mode (or that which you've set for the image) See [here](https://love2d.org/wiki/WrapMode) for an example of clamp. This can help with bleeding in some cases. This function will use the individual warp mode of each image, be default this is \"clamp\". You can change this by setting the warp mode of each image you give to the texture atlas.\n```lua\nta:setExtrude(1)\nta:setExtrude(2)\n```\n### textureAtlas:setSpacing(spacing)\nSets spacing between each image, doesn't add spacing between images and the edge of the atlas.\n```lua\nta:setSpacing(1)\nta:setSpacing(3)\n```\n### textureAtlas:setMaxSize(width=systemMax, height=systemMax)\nSet the maximum size for the texture atlas. Default is system limits, unless `love.graphics` isn't available (i.e. headless mode) then the default limit is 16,384.\n\nDue to constraints of `fixedSize`'s packing algorithm, if it cannot have the size it wants, it will throw an error. It cannot rearrange itself to fit in a restricted area.\n```lua\nta:setMaxSize(1000, 1000)\nta:setMaxSize(100) -- height will default to baseAtlas._maxCanvasSize\nta:setMaxSize(nil, 100) -- width will default to baseAtlas._maxCanvasSize\n```\n### textureAtlas:add(image, id, bake = false, ...)\nAdd or replace an image to your atlas. Use the 3rd argument to bake the addition. Recommended to only bake once all changes have been made - useful for updating one image. 4th argument is passed to `textureAtlas.bake`\n\n**Note, id can be any normal table index variable type - not limited to strings**\n```lua\nta:add(image, id, bake = false, ...)\nta:add(love.graphics.newImage(\"duck.png\"), \"duck\")\n\nfixed:add(love.graphics.newImage(\"duck.png\"), \"duck\", true)\ndynamic:add(love.graphics.newImage(\"duck.png\"), \"duck\", true, \"height\") -- option to add in sorting algorithm\n```\n### textureAtlas:remove(id, bake = false, ...)\nRemove an image added to the atlas. Use the 2nd argument to bake the removal. Recommended to only bake once all changes have been made or if you're only making a single change. 4th argument is passed to `textureAtlas.bake`\n```lua\nta:remove(id, bake = false, ...)\nta:remove(\"duck\")\n\nfixed:remove(\"duck\", true)\ndynamic:remove(\"duck\", true, \"area\")\n```\n### textureAtlas:getViewport(id)\nGet viewport for given id. Returns position, width and height for it's location on the texture atlas. Will throw an error if a quad doesn't exist for given id.\n```lua\nlocal x, y, w, h = ta:getViewport(id)\nlocal x, y, w, h = ta:getViewport(\"duck\")\n```\n### textureAtlas:bake\nBaking takes all added images and stitches them together onto a single image. Basic check in place to ensure it only bakes when changes have been made via `add` or `remove` to avoid needless baking\n\n**Note, it's recommended to use `textureAtlas:hardBake` once all changes have been made.**\n```lua\nfixed:bake()\ndynamic:bake(sortby)\ndynamic:bake(\"area\") \n-- _sortBy options: \"area\" (default), \"height\", \"width\", \"none\"\n-- \"height\" and \"area\" are best from unit testing - but do your own tests to see what works best for your images\n-- use dynamic.image to grab the baked image\n```\n### textureAtlas:hardBake\nHard baking bakes the image(if changes have been made since last bake) and removes all references to all added images. Once called, you cannot add(throws error), remove(throws error) or bake again(no error). This function is designed to free up unused memory.\n\n**Note, any references to images that still exist outside of textureAtlas will keep the image alive (`image:release` is not called)**\n\nCall `collectgarbage(\"collect\")` after `textureAtlas:hardBake` if you want to see instant results of cleaning out unused memory, otherwise let lua handle when it wants to collect garbage.\n```lua\nfixed:hardBake()\ndynamic:hardBake(sortBy) -- See textureAtlas:bake for sortBy options\n```\n### textureAtlas:draw(id, ...)\nDraw the given id, with the given variables. Varargs passed to `love.graphics.draw`\n```lua\nta:draw(\"cat\")\nta:draw(\"bird\", 50,50, 0, 5,5) -- draw id \"bird\" at 50,50 at scale 5\n```\n### textureAtlas:getDrawFuncForID(id)\nGet a draw function to avoid passing given texture atlas and id around\n```lua\nlocal draw = ta:getDrawFuncForID(\"duck\")\n-- draw(...) -- values are sent as arguments to love.graphics.draw, similar to textureAtlas:draw(id, ...)\ndraw(50,50, 0, 5,5) -- draws id \"duck\" at 50,50 at scale 5\n```\n","funding_links":[],"categories":["Drawing"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FEngineerSmith%2FRuntime-TextureAtlas","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FEngineerSmith%2FRuntime-TextureAtlas","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FEngineerSmith%2FRuntime-TextureAtlas/lists"}