#include "Atlas.hpp" #include "Texture.hpp" #include "ImageData.hpp" #include "../../maths/LMPacker.hpp" #include Atlas::Atlas( std::unique_ptr image, std::unordered_map regions, bool prepare ) : texture(nullptr), image(std::move(image)), regions(regions) { if (prepare) { this->prepare(); } } Atlas::~Atlas() { } void Atlas::prepare() { texture = Texture::from(image.get()); } bool Atlas::has(const std::string& name) const { return regions.find(name) != regions.end(); } const UVRegion& Atlas::get(const std::string& name) const { return regions.at(name); } Texture* Atlas::getTexture() const { return texture.get(); } ImageData* Atlas::getImage() const { return image.get(); } void AtlasBuilder::add(const std::string& name, std::unique_ptr image) { entries.push_back(atlasentry{name, std::shared_ptr(image.release())}); names.insert(name); } bool AtlasBuilder::has(const std::string& name) const { return names.find(name) != names.end(); } std::unique_ptr AtlasBuilder::build(uint extrusion, bool prepare, uint maxResolution) { if (maxResolution == 0) { maxResolution = Texture::MAX_RESOLUTION; } auto sizes = std::make_unique(entries.size() * 2); uint index = 0; for (auto& entry : entries) { auto image = entry.image; sizes[index++] = image->getWidth(); sizes[index++] = image->getHeight(); } LMPacker packer(sizes.get(), entries.size()*2); sizes.reset(nullptr); uint width = 32; uint height = 32; while (!packer.buildCompact(width, height, extrusion)) { if (width > height) { height *= 2; } else { width *= 2; } if (width > maxResolution || height > maxResolution) { throw std::runtime_error( "max atlas resolution "+std::to_string(maxResolution)+" exceeded" ); } } auto canvas = std::make_unique(ImageFormat::rgba8888, width, height); std::unordered_map regions; std::vector rects = packer.getResult(); for (uint i = 0; i < entries.size(); i++) { const rectangle& rect = rects[i]; const atlasentry& entry = entries[rect.idx]; uint x = rect.x; uint y = rect.y; uint w = rect.width; uint h = rect.height; canvas->blit(entry.image.get(), rect.x, rect.y); for (uint j = 0; j < extrusion; j++) { canvas->extrude(x - j, y - j, w + j*2, h + j*2); } float unitX = 1.0f / width; float unitY = 1.0f / height; regions[entry.name] = UVRegion( unitX * x, unitY * y, unitX * (x + w), unitY * (y + h) ); } return std::make_unique(std::move(canvas), regions, prepare); }