diff --git a/deps/stb/stb_rect_pack.h b/deps/stb/stb_rect_pack.h index 63a5b15944..f31258adb9 100644 --- a/deps/stb/stb_rect_pack.h +++ b/deps/stb/stb_rect_pack.h @@ -1,46 +1,43 @@ -// stb_rect_pack.h - v0.06 - public domain - rectangle packing -// Sean Barrett 2014 -// -// Useful for e.g. packing rectangular textures into an atlas. -// Does not do rotation. -// -// 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 stdlib. -// Can override those by defining STBRP_SORT and STBRP_ASSERT. -// -// 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. -// -// Credits -// -// Library -// Sean Barrett -// Minor features -// Martins Mozeiko -// Bugfixes / warning fixes -// [your name could be here] -// -// Version history: -// -// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort -// 0.05: added STBRP_ASSERT to allow replacing assert -// 0.04: fixed minor bug in STBRP_LARGE_RECTS support -// 0.01: initial release - -////////////////////////////////////////////////////////////////////////////// -// -// INCLUDE SECTION -// +/* + * stb_rect_pack.h - v0.06 - public domain - rectangle packing + * Sean Barrett 2014 + * + * Useful for e.g. packing rectangular textures into an atlas. + * Does not do rotation. + * + * 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 stdlib. + * Can override those by defining STBRP_SORT and STBRP_ASSERT. + * + * 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. + * + * Credits + * + * Library + * Sean Barrett + * Minor features + * Martins Mozeiko + * Bugfixes / warning fixes + * [your name could be here] + * + * Version history: + * + * 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort + * 0.05: added STBRP_ASSERT to allow replacing assert + * 0.04: fixed minor bug in STBRP_LARGE_RECTS support + * 0.01: initial release +*/ #ifndef STB_INCLUDE_STB_RECT_PACK_H #define STB_INCLUDE_STB_RECT_PACK_H @@ -67,87 +64,89 @@ typedef int stbrp_coord; typedef unsigned short stbrp_coord; #endif -STBRP_DEF void stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); -// 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. -// -// Rectangles which are successfully packed have the 'was_packed' flag -// set to a non-zero value and 'x' and 'y' store the minimum location -// on each axis (i.e. bottom-left in cartesian coordinates, top-left -// if you imagine y increasing downwards). Rectangles which do not fit -// have the 'was_packed' flag set to 0. -// -// You should not try to access the 'rects' array from another thread -// while this function is running, as the function temporarily reorders -// the array while it executes. -// -// To pack into another rectangle, you need to call stbrp_init_target -// again. To continue packing into the same rectangle, you can call -// this function again. Calling this multiple times with multiple rect -// arrays will probably produce worse packing results than calling it -// a single time with the full rectangle array, but the option is -// available. +STBRP_DEF void stbrp_pack_rects (stbrp_context *context, + stbrp_rect *rects, int num_rects); + +/* 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. + * + * Rectangles which are successfully packed have the 'was_packed' flag + * set to a non-zero value and 'x' and 'y' store the minimum location + * on each axis (i.e. bottom-left in cartesian coordinates, top-left + * if you imagine y increasing downwards). Rectangles which do not fit + * have the 'was_packed' flag set to 0. + * + * You should not try to access the 'rects' array from another thread + * while this function is running, as the function temporarily reorders + * the array while it executes. + * + * To pack into another rectangle, you need to call stbrp_init_target + * again. To continue packing into the same rectangle, you can call + * this function again. Calling this multiple times with multiple rect + * arrays will probably produce worse packing results than calling it + * a single time with the full rectangle array, but the option is + * available. + */ struct stbrp_rect { - // reserved for your use: - int id; - - // input: - stbrp_coord w, h; - - // output: - stbrp_coord x, y; - int was_packed; // non-zero if valid packing - -}; // 16 bytes, nominally + int id; /* reserved for your use: */ + stbrp_coord w, h; /* input: */ + stbrp_coord x, y; /* output: */ + int was_packed; /* non-zero if valid packing */ +}; /* 16 bytes, nominally */ -STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); -// Initialize a rectangle packer to: -// pack a rectangle that is 'width' by 'height' in dimensions -// using temporary storage provided by the array 'nodes', which is 'num_nodes' long -// -// You must call this function every time you start packing into a new target. -// -// There is no "shutdown" function. The 'nodes' memory must stay valid for -// the following stbrp_pack_rects() call (or calls), but can be freed after -// the call (or calls) finish. -// -// Note: to guarantee best results, either: -// 1. make sure 'num_nodes' >= 'width' -// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' -// -// If you don't do either of the above things, widths will be quantized to multiples -// of small integers to guarantee the algorithm doesn't run out of temporary storage. -// -// If you do #2, then the non-quantized algorithm will be used, but the algorithm -// may run out of temporary storage and be unable to pack some rectangles. +STBRP_DEF void stbrp_init_target (stbrp_context *context, + int width, int height, stbrp_node *nodes, int num_nodes); + +/* Initialize a rectangle packer to: + * pack a rectangle that is 'width' by 'height' in dimensions + * using temporary storage provided by the array 'nodes', which is 'num_nodes' long + * + * You must call this function every time you start packing into a new target. + * + * There is no "shutdown" function. The 'nodes' memory must stay valid for + * the following stbrp_pack_rects() call (or calls), but can be freed after + * the call (or calls) finish. + * + * Note: to guarantee best results, either: + * 1. make sure 'num_nodes' >= 'width' + * or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' + * + * If you don't do either of the above things, widths will be quantized to multiples + * of small integers to guarantee the algorithm doesn't run out of temporary storage. + * + * If you do #2, then the non-quantized algorithm will be used, but the algorithm + * may run out of temporary storage and be unable to pack some rectangles. + */ STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); -// Optionally call this function after init but before doing any packing to -// change the handling of the out-of-temp-memory scenario, described above. -// If you call init again, this will be reset to the default (false). + +/* Optionally call this function after init but before doing any packing to + * change the handling of the out-of-temp-memory scenario, described above. + * If you call init again, this will be reset to the default (false). + */ STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); -// Optionally select which packing heuristic the library should use. Different -// heuristics will produce better/worse results for different data sets. -// If you call init again, this will be reset to the default. + +/* Optionally select which packing heuristic the library should use. Different + * heuristics will produce better/worse results for different data sets. + * If you call init again, this will be reset to the default. + */ enum { STBRP_HEURISTIC_Skyline_default=0, STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, - STBRP_HEURISTIC_Skyline_BF_sortHeight, + STBRP_HEURISTIC_Skyline_BF_sortHeight }; - -////////////////////////////////////////////////////////////////////////////// -// -// the details of the following structures don't matter to you, but they must -// be visible so you can handle the memory allocations for them +/* the details of the following structures don't matter to you, but they must + * be visible so you can handle the memory allocations for them + */ struct stbrp_node { @@ -165,7 +164,7 @@ struct stbrp_context int num_nodes; stbrp_node *active_head; stbrp_node *free_head; - stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' + stbrp_node extra[2]; /* we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' */ }; #ifdef __cplusplus @@ -174,10 +173,7 @@ struct stbrp_context #endif -////////////////////////////////////////////////////////////////////////////// -// -// IMPLEMENTATION SECTION -// +/* IMPLEMENTATION SECTION */ #ifdef STB_RECT_PACK_IMPLEMENTATION #ifndef STBRP_SORT @@ -192,12 +188,13 @@ struct stbrp_context enum { - STBRP__INIT_skyline = 1, + STBRP__INIT_skyline = 1 }; STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) { - switch (context->init_mode) { + switch (context->init_mode) + { case STBRP__INIT_skyline: STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); context->heuristic = heuristic; @@ -209,20 +206,22 @@ STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) { + /* if it's ok to run out of memory, then don't bother aligning them; + * this gives better packing, but may fail due to OOM (even though + * the rectangles easily fit). @TODO a smarter approach would be to only + * quantize once we've hit OOM, then we could get rid of this parameter. + */ if (allow_out_of_mem) - // if it's ok to run out of memory, then don't bother aligning them; - // this gives better packing, but may fail due to OOM (even though - // the rectangles easily fit). @TODO a smarter approach would be to only - // quantize once we've hit OOM, then we could get rid of this parameter. context->align = 1; - else { - // if it's not ok to run out of memory, then quantize the widths - // so that num_nodes is always enough nodes. - // - // I.e. num_nodes * align >= width - // align >= width / num_nodes - // align = ceil(width/num_nodes) - + else + { + /* if it's not ok to run out of memory, then quantize the widths + * so that num_nodes is always enough nodes. + * + * I.e. num_nodes * align >= width + * align >= width / num_nodes + * align = ceil(width/num_nodes) + */ context->align = (context->width + context->num_nodes-1) / context->num_nodes; } } @@ -246,7 +245,8 @@ STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, context->num_nodes = num_nodes; stbrp_setup_allow_out_of_mem(context, 0); - // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) + /* node 0 is the full width, + * node 1 is the sentinel (lets us not store width explicitly) */ context->extra[0].x = 0; context->extra[0].y = 0; context->extra[0].next = &context->extra[1]; @@ -259,41 +259,41 @@ STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, context->extra[1].next = NULL; } -// find minimum y position if it starts at x1 -static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) +/* Find minimum y position if it starts at x1 */ +static int stbrp__skyline_find_min_y(stbrp_context *c, + stbrp_node *first, int x0, int width, int *pwaste) { + int min_y, visited_width, waste_area; stbrp_node *node = first; int x1 = x0 + width; - int min_y, visited_width, waste_area; + STBRP_ASSERT(first->x <= x0); - - #if 0 - // skip in case we're past the node - while (node->next->x <= x0) - ++node; - #else - STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency - #endif - + STBRP_ASSERT(node->next->x > x0); STBRP_ASSERT(node->x <= x0); min_y = 0; waste_area = 0; visited_width = 0; - 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 + 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 + + /* 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 + } + else + { + /* add waste area */ int under_width = node->next->x - node->x; if (under_width + visited_width > width) under_width = width - visited_width; @@ -319,27 +319,35 @@ static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int widt stbrp__findresult fr; stbrp_node **prev, *node, *tail, **best = NULL; - // align to multiple of c->align + /* align to multiple of c->align */ width = (width + c->align - 1); width -= width % c->align; STBRP_ASSERT(width % c->align == 0); node = c->active_head; prev = &c->active_head; - 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 - // bottom left - if (y < best_y) { + while (node->x + width <= c->width) + { + int waste; + int 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 bottom left */ + if (y < best_y) + { best_y = y; best = prev; } - } else { - // best-fit - if (y + height <= c->height) { - // can only use it if it first vertically - if (y < best_y || (y == best_y && waste < best_waste)) { + } + else + { + /* best-fit */ + if (y + height <= c->height) + { + /* can only use it if it first vertically */ + if (y < best_y || (y == best_y && waste < best_waste)) + { best_y = y; best_waste = waste; best = prev; @@ -352,44 +360,54 @@ static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int widt best_x = (best == NULL) ? 0 : (*best)->x; - // if doing best-fit (BF), we also have to try aligning right edge to each node position - // - // e.g, if fitting - // - // ____________________ - // |____________________| - // - // into - // - // | | - // | ____________| - // |____________| - // - // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned - // - // This makes BF take about 2x the time + /* if doing best-fit (BF), we also have to try aligning right edge to each node position + * + * e.g, if fitting + * + * ____________________ + * |____________________| + * + * into + * + * | | + * | ____________| + * |____________| + * + * then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned + * + * This makes BF take about 2x the time + */ - if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { + if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) + { tail = c->active_head; node = c->active_head; prev = &c->active_head; - // find first node that's admissible + /* find first node that's admissible */ while (tail->x < width) tail = tail->next; - while (tail) { + while (tail) + { int xpos = tail->x - width; int y,waste; STBRP_ASSERT(xpos >= 0); - // find the left position that matches this - while (node->next->x <= xpos) { + + /* find the left position that matches this */ + while (node->next->x <= xpos) + { prev = &node->next; node = node->next; } + STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); - if (y + height < c->height) { - if (y <= best_y) { - if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { + + if (y + height < c->height) + { + if (y <= best_y) + { + if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) + { best_x = xpos; STBRP_ASSERT(y <= best_y); best_y = y; @@ -410,83 +428,62 @@ static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int widt static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) { - // find best position according to heuristic - stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); + /* find best position according to heuristic */ stbrp_node *node, *cur; + stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); - // bail if: - // 1. it failed - // 2. the best node doesn't fit (we don't always check this) - // 3. we're out of memory - if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { + /* bail if: + * 1. it failed + * 2. the best node doesn't fit (we don't always check this) + * 3. we're out of memory + */ + if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) + { res.prev_link = NULL; return res; } - // on success, create new node + /* on success, create new node */ node = context->free_head; node->x = (stbrp_coord) res.x; node->y = (stbrp_coord) (res.y + height); context->free_head = node->next; - // insert the new node into the right starting point, and - // let 'cur' point to the remaining nodes needing to be - // stiched back in + /* insert the new node into the right starting point, and + * let 'cur' point to the remaining nodes needing to be + * stiched back in + */ cur = *res.prev_link; - if (cur->x < res.x) { - // preserve the existing one, so start testing with the next one + if (cur->x < res.x) + { + /* preserve the existing one, so start testing with the next one */ stbrp_node *next = cur->next; cur->next = node; cur = next; - } else { - *res.prev_link = node; } + else + *res.prev_link = node; - // from here, traverse cur and free the nodes, until we get to one - // that shouldn't be freed - while (cur->next && cur->next->x <= res.x + width) { + /* from here, traverse cur and free the nodes, until we get to one + * that shouldn't be freed */ + while (cur->next && cur->next->x <= res.x + width) + { stbrp_node *next = cur->next; - // move the current node to the free list + + /* move the current node to the free list */ cur->next = context->free_head; context->free_head = cur; cur = next; } - // stitch the list back in + /* stitch the list back in */ node->next = cur; if (cur->x < res.x + width) cur->x = (stbrp_coord) (res.x + width); -#ifdef _DEBUG - cur = context->active_head; - while (cur->x < context->width) { - STBRP_ASSERT(cur->x < cur->next->x); - cur = cur->next; - } - STBRP_ASSERT(cur->next == NULL); - - { - stbrp_node *L1 = NULL, *L2 = NULL; - int count=0; - cur = context->active_head; - while (cur) { - L1 = cur; - cur = cur->next; - ++count; - } - cur = context->free_head; - while (cur) { - L2 = cur; - cur = cur->next; - ++count; - } - STBRP_ASSERT(count == context->num_nodes+2); - } -#endif - return res; } @@ -529,20 +526,23 @@ STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int n { int i; - // we use the 'was_packed' field internally to allow sorting/unsorting - for (i=0; i < num_rects; ++i) { + /* we use the 'was_packed' field internally to allow sorting/unsorting */ + for (i=0; i < num_rects; ++i) + { rects[i].was_packed = i; #ifndef STBRP_LARGE_RECTS STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff); #endif } - // sort according to heuristic + /* sort according to heuristic */ STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); - for (i=0; i < num_rects; ++i) { + for (i=0; i < num_rects; ++i) + { stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); - if (fr.prev_link) { + if (fr.prev_link) + { rects[i].x = (stbrp_coord) fr.x; rects[i].y = (stbrp_coord) fr.y; } else { @@ -550,10 +550,10 @@ STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int n } } - // unsort + /* unsort */ STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); - // set was_packed flags + /* set was_packed flags */ for (i=0; i < num_rects; ++i) rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); }