fixed bugs related to the best-fit heuristic

This commit is contained in:
Sean Barrett 2014-11-24 18:12:46 -08:00
parent 0c10e7c40e
commit 8d3ef72e3c
1 changed files with 25 additions and 12 deletions

View File

@ -1,17 +1,24 @@
// stb_rect_pack.h - v0.01 - public domain - rectangle packing
// Sean Barrett 2014
//
// Useful for e.g. packing rectangular textures into an atlas.
// Does not do rotation.
//
// By Sean Barrett and Ryan Gordon
// Not necessarily the awesomest packing method, but better than
// the totally naive one in stb_truetype (which is primarily what
// this is meant to replace).
//
// Has only had a few tests run, may have issues.
//
// More docs to come.
//
// No memory allocations; uses qsort() and assert() from stblib.
//
// This library currently uses the Skyline Bottom-Left algorithm.
//
// Please note: better rectangle packers are welcome! Please
// implement them to the same API, but with a different init
// function. Contact me for details of how to set up the
// heuristic enums and suchlike (as the code currently isn't
// designed to do that correctly internally).
//
// function.
#ifndef STB_INCLUDE_STB_RECT_PACK_H
#define STB_INCLUDE_STB_RECT_PACK_H
@ -37,7 +44,7 @@ typedef unsigned short stbrp_coord;
#endif
STBRP_DEF void stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
// Assign packing locations to rectangles. The rectangles are of type
// Assign packed locations to rectangles. The rectangles are of type
// 'stbrp_rect' defined below, stored in the array 'rects', and there
// are 'num_rects' many of them.
//
@ -139,6 +146,7 @@ STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
switch (context->init_mode) {
case STBRP__INIT_skyline:
assert(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
context->heuristic = heuristic;
break;
default:
assert(0);
@ -218,21 +226,26 @@ static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0
min_y = 0;
waste_area = 0;
visited_width = 0;
while (node->x <= x1) {
while (node->x < x1) {
if (node->y > min_y) {
// raise min_y higher.
// we've accounted for all waste up to min_y,
// but we'll now add more waste for everything we've visted
waste_area += visited_width * (node->y - min_y);
min_y = node->y;
// the first time through, visited_width might be reduced
if (node->x < x0)
visited_width += node->next->x - x0;
else
visited_width += node->next->x - node->x;
} else {
// add waste area
int under_width = node->next->x - node->x;
if (under_width + visited_width > width)
under_width = width - visited_width;
waste_area += under_width * (min_y - node->y);
visited_width += under_width;
}
visited_width += node->next->x - node->x; // adds too much the last time, but that's never used
node = node->next;
}
@ -259,7 +272,7 @@ static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int widt
node = c->active_head;
prev = &c->active_head;
while (node->x + width < c->width) {
while (node->x + width <= c->width) {
int y,waste;
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
@ -309,13 +322,13 @@ static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int widt
// find first node that's admissible
while (tail->x < width)
tail = tail->next;
while (tail->x <= c->width) {
while (tail) {
int xpos = tail->x - width;
int y,waste;
assert(xpos >= 0);
// find the left position that matches this
while (node->next->x <= xpos) {
prev = &node;
prev = &node->next;
node = node->next;
}
assert(node->next->x > xpos && node->x <= xpos);
@ -376,7 +389,7 @@ static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, i
// from here, traverse cur and free the nodes, until we get to one
// that shouldn't be freed
while (cur->next->x <= res.x + width) {
while (cur->next && cur->next->x <= res.x + width) {
stbrp_node *next = cur->next;
// move the current node to the free list
cur->next = context->free_head;