AtlasGen: Separate pack and resolve steps

This commit is contained in:
Henrik Rydgård committed 2025-11-15 15:26:06 +01:00
1 parent 55d53bd29a
commit 915a88df60
4 files changed
+25 -11

No files matched your search

+9 -6
View File
@@ -100,12 +100,11 @@ inline bool CompareByArea(const Data& lhs, const Data& rhs) {
return lhs.w * lhs.h > rhs.w * rhs.h;
}
std::vector<Data> Bucket::Resolve(int image_width, Image *dest) {
void Bucket::Pack(int image_width) {
// Place all the little images - whatever they are.
// Uses greedy fill algorithm. Slow but works surprisingly well, CPUs are fast.
ImageU8 masq;
masq.resize(image_width, 1);
dest->resize(image_width, 1);
// image_width is set to the square root of the total area of all images.
// We shouldn't need more than twice that in height (more likely much less).
@@ -120,11 +119,10 @@ std::vector<Data> Bucket::Resolve(int image_width, Image *dest) {
int idy = (int)data[i].h;
if (idx > 1 && idy > 1) {
assert(idx <= image_width);
for (int ty = 0; ty < maxHeight - 1; ty++) { // TODO: Maybe remove this limit?
if (ty + idy + 1 > (int)dest->height()) {
for (int ty = 0; ty < maxHeight - 1; ty++) {
if (ty + idy + 1 > (int)masq.height()) {
// Every 16 lines of new space needed, grow the image.
masq.resize(image_width, ty + idy + 16);
dest->resize(image_width, ty + idy + 16);
}
// Brute force packing.
int sz = (int)data[i].w;
@@ -163,11 +161,16 @@ std::vector<Data> Bucket::Resolve(int image_width, Image *dest) {
// Sort the data back by ID.
std::sort(data.begin(), data.end(), CompareByID);
w = image_width;
h = masq.height();
}
std::vector<Data> Bucket::Resolve(Image *dest) {
dest->resize(w, h);
// Actually copy the image data in place, after doing the layout.
for (int i = 0; i < (int)data.size(); i++) {
dest->copyfrom(images[i], data[i].sx, data[i].sy, data[i].redToWhiteAlpha);
}
return data;
}
+6 -1
View File
@@ -119,12 +119,17 @@ struct Data {
struct Bucket {
std::vector<Image> images;
std::vector<Data> data;
int w = 0;
int h = 0;
void AddItem(Image &&img, const Data &dat) {
images.emplace_back(std::move(img));
data.emplace_back(dat);
}
void AddImage(Image &&img, int id);
std::vector<Data> Resolve(int image_width, Image *dest);
void Pack(int image_width);
std::vector<Data> Resolve(Image *dest);
};
AtlasImage ToAtlasImage(int id, std::string_view name, float tw, float th, const std::vector<Data> &results);
+6 -2
View File
@@ -385,8 +385,12 @@ static bool GenerateUIAtlasImage(Atlas *atlas, float dpiScale, Image *dest, int
int imageWidth = RoundToNextPowerOf2((int)sqrtf(area));
Instant bucketStart = Instant::Now();
std::vector<Data> results = bucket.Resolve(imageWidth, dest);
INFO_LOG(Log::G3D, " - Bucketed %zu images in %.2f ms (final image size: %dx%d)", results.size(), bucketStart.ElapsedMs(), dest->width(), dest->height());
bucket.Pack(imageWidth);
INFO_LOG(Log::G3D, " - Packed in %.2f ms (image size: %dx%d)", bucketStart.ElapsedMs(), bucket.w, bucket.h);
Instant resolveStart = Instant::Now();
std::vector<Data> results = bucket.Resolve(dest);
INFO_LOG(Log::G3D, " - Resolved %zu images in %.2f ms (final image size: %dx%d)", results.size(), resolveStart.ElapsedMs(), dest->width(), dest->height());
_dbg_assert_(!results.empty());
// Fill out the atlas structure.
+4 -2
View File
@@ -652,9 +652,11 @@ int GenerateFromScript(const char *script_file, const char *atlas_name, bool hig
// Place the subimages onto the main texture. Also writes to png.
Image dest;
// Place things on the bitmap.
printf("Resolving...\n");
std::vector<Data> results = bucket.Resolve(image_width, &dest);
printf("Packing...\n");
bucket.Pack(image_width);
printf("Resolving...\n");
std::vector<Data> results = bucket.Resolve(&dest);
if (highcolor) {
printf("Writing .ZIM %ix%i RGBA8888...\n", dest.width(), dest.height());
dest.SaveZIM(image_name.c_str(), ZIM_RGBA8888 | ZIM_ZSTD_COMPRESSED);