2D rectangular bin packing with option for item removal
C++
128
62 commits
updated Sep 4, 2026
2D rectangular bin packing utility that uses the Shelf Best Height Fit heuristic, and supports item removal. You could also call it a dynamic texture atlas allocator.
smol-atlas is a C++ library for packing small rectangles into a large rectangle.
Possible application is to assemble icons, glyphs or thumbnails into a larger
texture atlas.
This sounds simple, but finding an optimal solution is actually very hard.
There are many ways to approach the bin packing problem, but smol-atlas uses the Shelf Best
Height Fit heuristic. It works by dividing the total space into "shelves", each with a certain height.
The allocator packs rectangles onto whichever shelf minimizes the amount of wasted vertical space.
smol-atlas is simple, fast, and works best when the rectangles have similar heights (icons, glyphs
and thumbnails are like this). It is not a generalized bin packer, and can potentially waste a
lot of space if the rectangles vary significantly in height.
Implementation uses STL <vector>, and some manual memory allocation
with just regular new and delete. Custom allocators might be nice to
do someday.
At least C++11 is required (note: others have ported it to C).
License is either MIT or Unlicense, whichever is more convenient for you.
Take src/smol-atlas.cpp and src/smol-atlas.h, plop them into your project and build.
Include src/smol-atlas.h and use functions from there. Something like:
smol_atlas_t* atlas = sma_atlas_create(100, 100);
// add a 70x30 item
smol_atlas_item_t* item = sma_item_add(atlas, 70, 30);
if (item) {
// where did it end up?
int x = sma_item_x(item);
int y = sma_item_y(item);
// can also remove it at some point
sma_item_remove(atlas, item);
}
sma_atlas_destroy(atlas);
Do not use CMakeLists.txt at the root of this repository! That one is for building the "test / benchmark"
application, which also compiles several other texture packing libraries, and runs various tests on them.
I don't know!
But, I did test it on a use case I have in mind. Within Blender video sequence editor, I took previs timeline of Blender Studio Gold project, turned thumbnails on, and zoomed & panned around it for a while. During all that time, I dumped data of all the thumbnails that would be needed at any point.
The test then is this:
250 frames of this data, ran 30 times in a loop, featuring 4700 unique thumbnails, produces 160 thousand item additions and 150 thousand item removals from the texture atlas. All of the tested libraries produce an atlas of 1536x1536 pixels. "Win" time is Ryzen 5950X (VS2022), "Mac" time is M1 Max (Xcode15).
| Library | GCs | Repacks/grows | Allocs | Mac time, ms | Win time, ms | Look |
|---|---|---|---|---|---|---|
| smol-atlas | 800 | 127 | 168 | 9 | 10 | |
| Étagère (Rust!) from Nicolas Silva / Mozilla | 876 | 185 | 738 | 13 | 15 | |
| shelf-pack-cpp from Mapbox | 1027 | 426 | 521051 | 54 | 70 | |
| stb_rect_pack from Sean Barrett | 576 | 578 | 610 | 97 | 114 | |
| RectAllocator from Andrew Willmott | 912 | 248 | 331 | 306 | 387 |
My strategy for atlas resizing is the same for all the cases tested.
stb_rect_pack) "actually work",
since it gives them a chance to "clean up" all the would-be-unused space.smol-atlas seems to be a tiny bit faster than Étagère, faster than Mapbox shelf-pack-cpp, and quite a lot
faster than the slightly mis-used STB stb_rect_pack library ("mis-used" because it does not natively support
item removal).
Yes it does look good (github / blog post).
Unlike smol-atlas, it is presumably way better tested, given that it is part of Firefox.
But it is written in Rust, which may or might not suit your needs.
For testing it here, I compiled it as a shared library to be used from C. Notes to myself how I did it:
Build the dynamic libraries locally by:
Cargo.toml and add this section:
[lib]
crate-type = ["cdylib"]
.cargo/config.toml with contents:
[target.aarch64-apple-darwin]
rustflags = ["-C", "link-args=-Wl,-install_name,@rpath/libetagere.dylib"]
cargo build --release --features "ffi", it will be under target/release.cbindgen --config cbindgen.toml --crate etagere --output etagere.h. You might need to do
cargo install cbindgen first.external/etagere of this project, for Windows (x64) and macOS (arm64).smol-atlas, it uses a shelf packing algorithm.shelf-pack Javascript library from Mapbox.Not sure if any of this will happen, but here's a list of things that would be interesting to try:
62 commits
C++
91.3%
C
5.7%
CMake
3.0%
2D rectangular bin packing with option for item removal
C++
128
62 commits
updated Sep 4, 2026
2D rectangular bin packing utility that uses the Shelf Best Height Fit heuristic, and supports item removal. You could also call it a dynamic texture atlas allocator.
smol-atlas is a C++ library for packing small rectangles into a large rectangle.
Possible application is to assemble icons, glyphs or thumbnails into a larger
texture atlas.
This sounds simple, but finding an optimal solution is actually very hard.
There are many ways to approach the bin packing problem, but smol-atlas uses the Shelf Best
Height Fit heuristic. It works by dividing the total space into "shelves", each with a certain height.
The allocator packs rectangles onto whichever shelf minimizes the amount of wasted vertical space.
smol-atlas is simple, fast, and works best when the rectangles have similar heights (icons, glyphs
and thumbnails are like this). It is not a generalized bin packer, and can potentially waste a
lot of space if the rectangles vary significantly in height.
Implementation uses STL <vector>, and some manual memory allocation
with just regular new and delete. Custom allocators might be nice to
do someday.
At least C++11 is required (note: others have ported it to C).
License is either MIT or Unlicense, whichever is more convenient for you.
Take src/smol-atlas.cpp and src/smol-atlas.h, plop them into your project and build.
Include src/smol-atlas.h and use functions from there. Something like:
smol_atlas_t* atlas = sma_atlas_create(100, 100);
// add a 70x30 item
smol_atlas_item_t* item = sma_item_add(atlas, 70, 30);
if (item) {
// where did it end up?
int x = sma_item_x(item);
int y = sma_item_y(item);
// can also remove it at some point
sma_item_remove(atlas, item);
}
sma_atlas_destroy(atlas);
Do not use CMakeLists.txt at the root of this repository! That one is for building the "test / benchmark"
application, which also compiles several other texture packing libraries, and runs various tests on them.
I don't know!
But, I did test it on a use case I have in mind. Within Blender video sequence editor, I took previs timeline of Blender Studio Gold project, turned thumbnails on, and zoomed & panned around it for a while. During all that time, I dumped data of all the thumbnails that would be needed at any point.
The test then is this:
250 frames of this data, ran 30 times in a loop, featuring 4700 unique thumbnails, produces 160 thousand item additions and 150 thousand item removals from the texture atlas. All of the tested libraries produce an atlas of 1536x1536 pixels. "Win" time is Ryzen 5950X (VS2022), "Mac" time is M1 Max (Xcode15).
| Library | GCs | Repacks/grows | Allocs | Mac time, ms | Win time, ms | Look |
|---|---|---|---|---|---|---|
| smol-atlas | 800 | 127 | 168 | 9 | 10 | |
| Étagère (Rust!) from Nicolas Silva / Mozilla | 876 | 185 | 738 | 13 | 15 | |
| shelf-pack-cpp from Mapbox | 1027 | 426 | 521051 | 54 | 70 | |
| stb_rect_pack from Sean Barrett | 576 | 578 | 610 | 97 | 114 | |
| RectAllocator from Andrew Willmott | 912 | 248 | 331 | 306 | 387 |
My strategy for atlas resizing is the same for all the cases tested.
stb_rect_pack) "actually work",
since it gives them a chance to "clean up" all the would-be-unused space.smol-atlas seems to be a tiny bit faster than Étagère, faster than Mapbox shelf-pack-cpp, and quite a lot
faster than the slightly mis-used STB stb_rect_pack library ("mis-used" because it does not natively support
item removal).
Yes it does look good (github / blog post).
Unlike smol-atlas, it is presumably way better tested, given that it is part of Firefox.
But it is written in Rust, which may or might not suit your needs.
For testing it here, I compiled it as a shared library to be used from C. Notes to myself how I did it:
Build the dynamic libraries locally by:
Cargo.toml and add this section:
[lib]
crate-type = ["cdylib"]
.cargo/config.toml with contents:
[target.aarch64-apple-darwin]
rustflags = ["-C", "link-args=-Wl,-install_name,@rpath/libetagere.dylib"]
cargo build --release --features "ffi", it will be under target/release.cbindgen --config cbindgen.toml --crate etagere --output etagere.h. You might need to do
cargo install cbindgen first.external/etagere of this project, for Windows (x64) and macOS (arm64).smol-atlas, it uses a shelf packing algorithm.shelf-pack Javascript library from Mapbox.Not sure if any of this will happen, but here's a list of things that would be interesting to try:
62 commits
C++
91.3%
C
5.7%
CMake
3.0%