Expand dynamic atlas without rearranging previous glyphs

This commit is contained in:
Chlumsky 2021-12-15 23:55:13 +01:00
parent dadc65c85c
commit a99a4575a4
4 changed files with 29 additions and 7 deletions

View File

@ -27,7 +27,7 @@ public:
/// Creates with a configured generator. The generator must not contain any prior glyphs!
explicit DynamicAtlas(AtlasGenerator &&generator);
/// Adds a batch of glyphs. Adding more than one glyph at a time may improve packing efficiency
ChangeFlags add(GlyphGeometry *glyphs, int count);
ChangeFlags add(GlyphGeometry *glyphs, int count, bool allowRearrange = false);
/// Allows access to generator. Do not add glyphs to the generator directly!
AtlasGenerator & atlasGenerator();
const AtlasGenerator & atlasGenerator() const;

View File

@ -10,7 +10,7 @@ template <class AtlasGenerator>
DynamicAtlas<AtlasGenerator>::DynamicAtlas(AtlasGenerator &&generator) : generator((AtlasGenerator &&) generator), glyphCount(0), side(0), totalArea(0), padding(0) { }
template <class AtlasGenerator>
typename DynamicAtlas<AtlasGenerator>::ChangeFlags DynamicAtlas<AtlasGenerator>::add(GlyphGeometry *glyphs, int count) {
typename DynamicAtlas<AtlasGenerator>::ChangeFlags DynamicAtlas<AtlasGenerator>::add(GlyphGeometry *glyphs, int count, bool allowRearrange) {
ChangeFlags changeFlags = 0;
int start = rectangles.size();
for (int i = 0; i < count; ++i) {
@ -29,16 +29,22 @@ typename DynamicAtlas<AtlasGenerator>::ChangeFlags DynamicAtlas<AtlasGenerator>:
}
if ((int) rectangles.size() > start) {
int packerStart = start;
while (packer.pack(rectangles.data()+packerStart, rectangles.size()-packerStart) > 0) {
int remaining;
while ((remaining = packer.pack(rectangles.data()+packerStart, rectangles.size()-packerStart)) > 0) {
side = (side+!side)<<1;
while (side*side < totalArea)
side <<= 1;
packer = RectanglePacker(side+padding, side+padding);
packerStart = 0;
if (allowRearrange) {
packer = RectanglePacker(side+padding, side+padding);
packerStart = 0;
} else {
packer.expand(side+padding, side+padding);
packerStart = rectangles.size()-remaining;
}
changeFlags |= RESIZED;
}
if (packerStart < start) {
for (int i = 0; i < start; ++i) {
for (int i = packerStart; i < start; ++i) {
Remap &remap = remapBuffer[i];
remap.source = remap.target;
remap.target.x = rectangles[i].x;

View File

@ -25,12 +25,26 @@ RectanglePacker::RectanglePacker(int width, int height) {
spaces.push_back(Rectangle { 0, 0, width, height });
}
void RectanglePacker::expand(int width, int height) {
if (width > 0 && height > 0) {
int oldWidth = 0, oldHeight = 0;
for (const Rectangle &space : spaces) {
if (space.x+space.w > oldWidth)
oldWidth = space.x+space.w;
if (space.y+space.h > oldHeight)
oldHeight = space.y+space.h;
}
spaces.push_back(Rectangle { 0, 0, width, height });
splitSpace(int(spaces.size()-1), oldWidth, oldHeight);
}
}
void RectanglePacker::splitSpace(int index, int w, int h) {
Rectangle space = spaces[index];
removeFromUnorderedVector(spaces, index);
Rectangle a = { space.x, space.y+h, w, space.h-h };
Rectangle b = { space.x+w, space.y, space.w-w, h };
if (w*(space.h-h) <= h*(space.w-w))
if (w*(space.h-h) < h*(space.w-w))
a.w = space.w;
else
b.h = space.h;

View File

@ -12,6 +12,8 @@ class RectanglePacker {
public:
RectanglePacker();
RectanglePacker(int width, int height);
/// Expands the packing area - both width and height must be greater or equal to the previous value
void expand(int width, int height);
/// Packs the rectangle array, returns how many didn't fit (0 on success)
int pack(Rectangle *rectangles, int count);
int pack(OrientedRectangle *rectangles, int count);