1b8e80941Smrg// [DEAR IMGUI] 2b8e80941Smrg// This is a slightly modified version of stb_rect_pack.h 0.99. 3b8e80941Smrg// Those changes would need to be pushed into nothings/stb: 4b8e80941Smrg// - Added STBRP__CDECL 5b8e80941Smrg// Grep for [DEAR IMGUI] to find the changes. 6b8e80941Smrg 7b8e80941Smrg// stb_rect_pack.h - v0.99 - public domain - rectangle packing 8b8e80941Smrg// Sean Barrett 2014 9b8e80941Smrg// 10b8e80941Smrg// Useful for e.g. packing rectangular textures into an atlas. 11b8e80941Smrg// Does not do rotation. 12b8e80941Smrg// 13b8e80941Smrg// Not necessarily the awesomest packing method, but better than 14b8e80941Smrg// the totally naive one in stb_truetype (which is primarily what 15b8e80941Smrg// this is meant to replace). 16b8e80941Smrg// 17b8e80941Smrg// Has only had a few tests run, may have issues. 18b8e80941Smrg// 19b8e80941Smrg// More docs to come. 20b8e80941Smrg// 21b8e80941Smrg// No memory allocations; uses qsort() and assert() from stdlib. 22b8e80941Smrg// Can override those by defining STBRP_SORT and STBRP_ASSERT. 23b8e80941Smrg// 24b8e80941Smrg// This library currently uses the Skyline Bottom-Left algorithm. 25b8e80941Smrg// 26b8e80941Smrg// Please note: better rectangle packers are welcome! Please 27b8e80941Smrg// implement them to the same API, but with a different init 28b8e80941Smrg// function. 29b8e80941Smrg// 30b8e80941Smrg// Credits 31b8e80941Smrg// 32b8e80941Smrg// Library 33b8e80941Smrg// Sean Barrett 34b8e80941Smrg// Minor features 35b8e80941Smrg// Martins Mozeiko 36b8e80941Smrg// github:IntellectualKitty 37b8e80941Smrg// 38b8e80941Smrg// Bugfixes / warning fixes 39b8e80941Smrg// Jeremy Jaussaud 40b8e80941Smrg// 41b8e80941Smrg// Version history: 42b8e80941Smrg// 43b8e80941Smrg// 0.99 (2019-02-07) warning fixes 44b8e80941Smrg// 0.11 (2017-03-03) return packing success/fail result 45b8e80941Smrg// 0.10 (2016-10-25) remove cast-away-const to avoid warnings 46b8e80941Smrg// 0.09 (2016-08-27) fix compiler warnings 47b8e80941Smrg// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) 48b8e80941Smrg// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) 49b8e80941Smrg// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort 50b8e80941Smrg// 0.05: added STBRP_ASSERT to allow replacing assert 51b8e80941Smrg// 0.04: fixed minor bug in STBRP_LARGE_RECTS support 52b8e80941Smrg// 0.01: initial release 53b8e80941Smrg// 54b8e80941Smrg// LICENSE 55b8e80941Smrg// 56b8e80941Smrg// See end of file for license information. 57b8e80941Smrg 58b8e80941Smrg////////////////////////////////////////////////////////////////////////////// 59b8e80941Smrg// 60b8e80941Smrg// INCLUDE SECTION 61b8e80941Smrg// 62b8e80941Smrg 63b8e80941Smrg#ifndef STB_INCLUDE_STB_RECT_PACK_H 64b8e80941Smrg#define STB_INCLUDE_STB_RECT_PACK_H 65b8e80941Smrg 66b8e80941Smrg#define STB_RECT_PACK_VERSION 1 67b8e80941Smrg 68b8e80941Smrg#ifdef STBRP_STATIC 69b8e80941Smrg#define STBRP_DEF static 70b8e80941Smrg#else 71b8e80941Smrg#define STBRP_DEF extern 72b8e80941Smrg#endif 73b8e80941Smrg 74b8e80941Smrg#ifdef __cplusplus 75b8e80941Smrgextern "C" { 76b8e80941Smrg#endif 77b8e80941Smrg 78b8e80941Smrgtypedef struct stbrp_context stbrp_context; 79b8e80941Smrgtypedef struct stbrp_node stbrp_node; 80b8e80941Smrgtypedef struct stbrp_rect stbrp_rect; 81b8e80941Smrg 82b8e80941Smrg#ifdef STBRP_LARGE_RECTS 83b8e80941Smrgtypedef int stbrp_coord; 84b8e80941Smrg#else 85b8e80941Smrgtypedef unsigned short stbrp_coord; 86b8e80941Smrg#endif 87b8e80941Smrg 88b8e80941SmrgSTBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); 89b8e80941Smrg// Assign packed locations to rectangles. The rectangles are of type 90b8e80941Smrg// 'stbrp_rect' defined below, stored in the array 'rects', and there 91b8e80941Smrg// are 'num_rects' many of them. 92b8e80941Smrg// 93b8e80941Smrg// Rectangles which are successfully packed have the 'was_packed' flag 94b8e80941Smrg// set to a non-zero value and 'x' and 'y' store the minimum location 95b8e80941Smrg// on each axis (i.e. bottom-left in cartesian coordinates, top-left 96b8e80941Smrg// if you imagine y increasing downwards). Rectangles which do not fit 97b8e80941Smrg// have the 'was_packed' flag set to 0. 98b8e80941Smrg// 99b8e80941Smrg// You should not try to access the 'rects' array from another thread 100b8e80941Smrg// while this function is running, as the function temporarily reorders 101b8e80941Smrg// the array while it executes. 102b8e80941Smrg// 103b8e80941Smrg// To pack into another rectangle, you need to call stbrp_init_target 104b8e80941Smrg// again. To continue packing into the same rectangle, you can call 105b8e80941Smrg// this function again. Calling this multiple times with multiple rect 106b8e80941Smrg// arrays will probably produce worse packing results than calling it 107b8e80941Smrg// a single time with the full rectangle array, but the option is 108b8e80941Smrg// available. 109b8e80941Smrg// 110b8e80941Smrg// The function returns 1 if all of the rectangles were successfully 111b8e80941Smrg// packed and 0 otherwise. 112b8e80941Smrg 113b8e80941Smrgstruct stbrp_rect 114b8e80941Smrg{ 115b8e80941Smrg // reserved for your use: 116b8e80941Smrg int id; 117b8e80941Smrg 118b8e80941Smrg // input: 119b8e80941Smrg stbrp_coord w, h; 120b8e80941Smrg 121b8e80941Smrg // output: 122b8e80941Smrg stbrp_coord x, y; 123b8e80941Smrg int was_packed; // non-zero if valid packing 124b8e80941Smrg 125b8e80941Smrg}; // 16 bytes, nominally 126b8e80941Smrg 127b8e80941Smrg 128b8e80941SmrgSTBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); 129b8e80941Smrg// Initialize a rectangle packer to: 130b8e80941Smrg// pack a rectangle that is 'width' by 'height' in dimensions 131b8e80941Smrg// using temporary storage provided by the array 'nodes', which is 'num_nodes' long 132b8e80941Smrg// 133b8e80941Smrg// You must call this function every time you start packing into a new target. 134b8e80941Smrg// 135b8e80941Smrg// There is no "shutdown" function. The 'nodes' memory must stay valid for 136b8e80941Smrg// the following stbrp_pack_rects() call (or calls), but can be freed after 137b8e80941Smrg// the call (or calls) finish. 138b8e80941Smrg// 139b8e80941Smrg// Note: to guarantee best results, either: 140b8e80941Smrg// 1. make sure 'num_nodes' >= 'width' 141b8e80941Smrg// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' 142b8e80941Smrg// 143b8e80941Smrg// If you don't do either of the above things, widths will be quantized to multiples 144b8e80941Smrg// of small integers to guarantee the algorithm doesn't run out of temporary storage. 145b8e80941Smrg// 146b8e80941Smrg// If you do #2, then the non-quantized algorithm will be used, but the algorithm 147b8e80941Smrg// may run out of temporary storage and be unable to pack some rectangles. 148b8e80941Smrg 149b8e80941SmrgSTBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); 150b8e80941Smrg// Optionally call this function after init but before doing any packing to 151b8e80941Smrg// change the handling of the out-of-temp-memory scenario, described above. 152b8e80941Smrg// If you call init again, this will be reset to the default (false). 153b8e80941Smrg 154b8e80941Smrg 155b8e80941SmrgSTBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); 156b8e80941Smrg// Optionally select which packing heuristic the library should use. Different 157b8e80941Smrg// heuristics will produce better/worse results for different data sets. 158b8e80941Smrg// If you call init again, this will be reset to the default. 159b8e80941Smrg 160b8e80941Smrgenum 161b8e80941Smrg{ 162b8e80941Smrg STBRP_HEURISTIC_Skyline_default=0, 163b8e80941Smrg STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, 164b8e80941Smrg STBRP_HEURISTIC_Skyline_BF_sortHeight 165b8e80941Smrg}; 166b8e80941Smrg 167b8e80941Smrg 168b8e80941Smrg////////////////////////////////////////////////////////////////////////////// 169b8e80941Smrg// 170b8e80941Smrg// the details of the following structures don't matter to you, but they must 171b8e80941Smrg// be visible so you can handle the memory allocations for them 172b8e80941Smrg 173b8e80941Smrgstruct stbrp_node 174b8e80941Smrg{ 175b8e80941Smrg stbrp_coord x,y; 176b8e80941Smrg stbrp_node *next; 177b8e80941Smrg}; 178b8e80941Smrg 179b8e80941Smrgstruct stbrp_context 180b8e80941Smrg{ 181b8e80941Smrg int width; 182b8e80941Smrg int height; 183b8e80941Smrg int align; 184b8e80941Smrg int init_mode; 185b8e80941Smrg int heuristic; 186b8e80941Smrg int num_nodes; 187b8e80941Smrg stbrp_node *active_head; 188b8e80941Smrg stbrp_node *free_head; 189b8e80941Smrg stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' 190b8e80941Smrg}; 191b8e80941Smrg 192b8e80941Smrg#ifdef __cplusplus 193b8e80941Smrg} 194b8e80941Smrg#endif 195b8e80941Smrg 196b8e80941Smrg#endif 197b8e80941Smrg 198b8e80941Smrg////////////////////////////////////////////////////////////////////////////// 199b8e80941Smrg// 200b8e80941Smrg// IMPLEMENTATION SECTION 201b8e80941Smrg// 202b8e80941Smrg 203b8e80941Smrg#ifdef STB_RECT_PACK_IMPLEMENTATION 204b8e80941Smrg#ifndef STBRP_SORT 205b8e80941Smrg#include <stdlib.h> 206b8e80941Smrg#define STBRP_SORT qsort 207b8e80941Smrg#endif 208b8e80941Smrg 209b8e80941Smrg#ifndef STBRP_ASSERT 210b8e80941Smrg#include <assert.h> 211b8e80941Smrg#define STBRP_ASSERT assert 212b8e80941Smrg#endif 213b8e80941Smrg 214b8e80941Smrg// [DEAR IMGUI] Added STBRP__CDECL 215b8e80941Smrg#ifdef _MSC_VER 216b8e80941Smrg#define STBRP__NOTUSED(v) (void)(v) 217b8e80941Smrg#define STBRP__CDECL __cdecl 218b8e80941Smrg#else 219b8e80941Smrg#define STBRP__NOTUSED(v) (void)sizeof(v) 220b8e80941Smrg#define STBRP__CDECL 221b8e80941Smrg#endif 222b8e80941Smrg 223b8e80941Smrgenum 224b8e80941Smrg{ 225b8e80941Smrg STBRP__INIT_skyline = 1 226b8e80941Smrg}; 227b8e80941Smrg 228b8e80941SmrgSTBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) 229b8e80941Smrg{ 230b8e80941Smrg switch (context->init_mode) { 231b8e80941Smrg case STBRP__INIT_skyline: 232b8e80941Smrg STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); 233b8e80941Smrg context->heuristic = heuristic; 234b8e80941Smrg break; 235b8e80941Smrg default: 236b8e80941Smrg STBRP_ASSERT(0); 237b8e80941Smrg } 238b8e80941Smrg} 239b8e80941Smrg 240b8e80941SmrgSTBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) 241b8e80941Smrg{ 242b8e80941Smrg if (allow_out_of_mem) 243b8e80941Smrg // if it's ok to run out of memory, then don't bother aligning them; 244b8e80941Smrg // this gives better packing, but may fail due to OOM (even though 245b8e80941Smrg // the rectangles easily fit). @TODO a smarter approach would be to only 246b8e80941Smrg // quantize once we've hit OOM, then we could get rid of this parameter. 247b8e80941Smrg context->align = 1; 248b8e80941Smrg else { 249b8e80941Smrg // if it's not ok to run out of memory, then quantize the widths 250b8e80941Smrg // so that num_nodes is always enough nodes. 251b8e80941Smrg // 252b8e80941Smrg // I.e. num_nodes * align >= width 253b8e80941Smrg // align >= width / num_nodes 254b8e80941Smrg // align = ceil(width/num_nodes) 255b8e80941Smrg 256b8e80941Smrg context->align = (context->width + context->num_nodes-1) / context->num_nodes; 257b8e80941Smrg } 258b8e80941Smrg} 259b8e80941Smrg 260b8e80941SmrgSTBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) 261b8e80941Smrg{ 262b8e80941Smrg int i; 263b8e80941Smrg#ifndef STBRP_LARGE_RECTS 264b8e80941Smrg STBRP_ASSERT(width <= 0xffff && height <= 0xffff); 265b8e80941Smrg#endif 266b8e80941Smrg 267b8e80941Smrg for (i=0; i < num_nodes-1; ++i) 268b8e80941Smrg nodes[i].next = &nodes[i+1]; 269b8e80941Smrg nodes[i].next = NULL; 270b8e80941Smrg context->init_mode = STBRP__INIT_skyline; 271b8e80941Smrg context->heuristic = STBRP_HEURISTIC_Skyline_default; 272b8e80941Smrg context->free_head = &nodes[0]; 273b8e80941Smrg context->active_head = &context->extra[0]; 274b8e80941Smrg context->width = width; 275b8e80941Smrg context->height = height; 276b8e80941Smrg context->num_nodes = num_nodes; 277b8e80941Smrg stbrp_setup_allow_out_of_mem(context, 0); 278b8e80941Smrg 279b8e80941Smrg // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) 280b8e80941Smrg context->extra[0].x = 0; 281b8e80941Smrg context->extra[0].y = 0; 282b8e80941Smrg context->extra[0].next = &context->extra[1]; 283b8e80941Smrg context->extra[1].x = (stbrp_coord) width; 284b8e80941Smrg#ifdef STBRP_LARGE_RECTS 285b8e80941Smrg context->extra[1].y = (1<<30); 286b8e80941Smrg#else 287b8e80941Smrg context->extra[1].y = 65535; 288b8e80941Smrg#endif 289b8e80941Smrg context->extra[1].next = NULL; 290b8e80941Smrg} 291b8e80941Smrg 292b8e80941Smrg// find minimum y position if it starts at x1 293b8e80941Smrgstatic int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) 294b8e80941Smrg{ 295b8e80941Smrg stbrp_node *node = first; 296b8e80941Smrg int x1 = x0 + width; 297b8e80941Smrg int min_y, visited_width, waste_area; 298b8e80941Smrg 299b8e80941Smrg STBRP__NOTUSED(c); 300b8e80941Smrg 301b8e80941Smrg STBRP_ASSERT(first->x <= x0); 302b8e80941Smrg 303b8e80941Smrg #if 0 304b8e80941Smrg // skip in case we're past the node 305b8e80941Smrg while (node->next->x <= x0) 306b8e80941Smrg ++node; 307b8e80941Smrg #else 308b8e80941Smrg STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency 309b8e80941Smrg #endif 310b8e80941Smrg 311b8e80941Smrg STBRP_ASSERT(node->x <= x0); 312b8e80941Smrg 313b8e80941Smrg min_y = 0; 314b8e80941Smrg waste_area = 0; 315b8e80941Smrg visited_width = 0; 316b8e80941Smrg while (node->x < x1) { 317b8e80941Smrg if (node->y > min_y) { 318b8e80941Smrg // raise min_y higher. 319b8e80941Smrg // we've accounted for all waste up to min_y, 320b8e80941Smrg // but we'll now add more waste for everything we've visted 321b8e80941Smrg waste_area += visited_width * (node->y - min_y); 322b8e80941Smrg min_y = node->y; 323b8e80941Smrg // the first time through, visited_width might be reduced 324b8e80941Smrg if (node->x < x0) 325b8e80941Smrg visited_width += node->next->x - x0; 326b8e80941Smrg else 327b8e80941Smrg visited_width += node->next->x - node->x; 328b8e80941Smrg } else { 329b8e80941Smrg // add waste area 330b8e80941Smrg int under_width = node->next->x - node->x; 331b8e80941Smrg if (under_width + visited_width > width) 332b8e80941Smrg under_width = width - visited_width; 333b8e80941Smrg waste_area += under_width * (min_y - node->y); 334b8e80941Smrg visited_width += under_width; 335b8e80941Smrg } 336b8e80941Smrg node = node->next; 337b8e80941Smrg } 338b8e80941Smrg 339b8e80941Smrg *pwaste = waste_area; 340b8e80941Smrg return min_y; 341b8e80941Smrg} 342b8e80941Smrg 343b8e80941Smrgtypedef struct 344b8e80941Smrg{ 345b8e80941Smrg int x,y; 346b8e80941Smrg stbrp_node **prev_link; 347b8e80941Smrg} stbrp__findresult; 348b8e80941Smrg 349b8e80941Smrgstatic stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) 350b8e80941Smrg{ 351b8e80941Smrg int best_waste = (1<<30), best_x, best_y = (1 << 30); 352b8e80941Smrg stbrp__findresult fr; 353b8e80941Smrg stbrp_node **prev, *node, *tail, **best = NULL; 354b8e80941Smrg 355b8e80941Smrg // align to multiple of c->align 356b8e80941Smrg width = (width + c->align - 1); 357b8e80941Smrg width -= width % c->align; 358b8e80941Smrg STBRP_ASSERT(width % c->align == 0); 359b8e80941Smrg 360b8e80941Smrg node = c->active_head; 361b8e80941Smrg prev = &c->active_head; 362b8e80941Smrg while (node->x + width <= c->width) { 363b8e80941Smrg int y,waste; 364b8e80941Smrg y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); 365b8e80941Smrg if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL 366b8e80941Smrg // bottom left 367b8e80941Smrg if (y < best_y) { 368b8e80941Smrg best_y = y; 369b8e80941Smrg best = prev; 370b8e80941Smrg } 371b8e80941Smrg } else { 372b8e80941Smrg // best-fit 373b8e80941Smrg if (y + height <= c->height) { 374b8e80941Smrg // can only use it if it first vertically 375b8e80941Smrg if (y < best_y || (y == best_y && waste < best_waste)) { 376b8e80941Smrg best_y = y; 377b8e80941Smrg best_waste = waste; 378b8e80941Smrg best = prev; 379b8e80941Smrg } 380b8e80941Smrg } 381b8e80941Smrg } 382b8e80941Smrg prev = &node->next; 383b8e80941Smrg node = node->next; 384b8e80941Smrg } 385b8e80941Smrg 386b8e80941Smrg best_x = (best == NULL) ? 0 : (*best)->x; 387b8e80941Smrg 388b8e80941Smrg // if doing best-fit (BF), we also have to try aligning right edge to each node position 389b8e80941Smrg // 390b8e80941Smrg // e.g, if fitting 391b8e80941Smrg // 392b8e80941Smrg // ____________________ 393b8e80941Smrg // |____________________| 394b8e80941Smrg // 395b8e80941Smrg // into 396b8e80941Smrg // 397b8e80941Smrg // | | 398b8e80941Smrg // | ____________| 399b8e80941Smrg // |____________| 400b8e80941Smrg // 401b8e80941Smrg // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned 402b8e80941Smrg // 403b8e80941Smrg // This makes BF take about 2x the time 404b8e80941Smrg 405b8e80941Smrg if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { 406b8e80941Smrg tail = c->active_head; 407b8e80941Smrg node = c->active_head; 408b8e80941Smrg prev = &c->active_head; 409b8e80941Smrg // find first node that's admissible 410b8e80941Smrg while (tail->x < width) 411b8e80941Smrg tail = tail->next; 412b8e80941Smrg while (tail) { 413b8e80941Smrg int xpos = tail->x - width; 414b8e80941Smrg int y,waste; 415b8e80941Smrg STBRP_ASSERT(xpos >= 0); 416b8e80941Smrg // find the left position that matches this 417b8e80941Smrg while (node->next->x <= xpos) { 418b8e80941Smrg prev = &node->next; 419b8e80941Smrg node = node->next; 420b8e80941Smrg } 421b8e80941Smrg STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); 422b8e80941Smrg y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); 423b8e80941Smrg if (y + height < c->height) { 424b8e80941Smrg if (y <= best_y) { 425b8e80941Smrg if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { 426b8e80941Smrg best_x = xpos; 427b8e80941Smrg STBRP_ASSERT(y <= best_y); 428b8e80941Smrg best_y = y; 429b8e80941Smrg best_waste = waste; 430b8e80941Smrg best = prev; 431b8e80941Smrg } 432b8e80941Smrg } 433b8e80941Smrg } 434b8e80941Smrg tail = tail->next; 435b8e80941Smrg } 436b8e80941Smrg } 437b8e80941Smrg 438b8e80941Smrg fr.prev_link = best; 439b8e80941Smrg fr.x = best_x; 440b8e80941Smrg fr.y = best_y; 441b8e80941Smrg return fr; 442b8e80941Smrg} 443b8e80941Smrg 444b8e80941Smrgstatic stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) 445b8e80941Smrg{ 446b8e80941Smrg // find best position according to heuristic 447b8e80941Smrg stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); 448b8e80941Smrg stbrp_node *node, *cur; 449b8e80941Smrg 450b8e80941Smrg // bail if: 451b8e80941Smrg // 1. it failed 452b8e80941Smrg // 2. the best node doesn't fit (we don't always check this) 453b8e80941Smrg // 3. we're out of memory 454b8e80941Smrg if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { 455b8e80941Smrg res.prev_link = NULL; 456b8e80941Smrg return res; 457b8e80941Smrg } 458b8e80941Smrg 459b8e80941Smrg // on success, create new node 460b8e80941Smrg node = context->free_head; 461b8e80941Smrg node->x = (stbrp_coord) res.x; 462b8e80941Smrg node->y = (stbrp_coord) (res.y + height); 463b8e80941Smrg 464b8e80941Smrg context->free_head = node->next; 465b8e80941Smrg 466b8e80941Smrg // insert the new node into the right starting point, and 467b8e80941Smrg // let 'cur' point to the remaining nodes needing to be 468b8e80941Smrg // stiched back in 469b8e80941Smrg 470b8e80941Smrg cur = *res.prev_link; 471b8e80941Smrg if (cur->x < res.x) { 472b8e80941Smrg // preserve the existing one, so start testing with the next one 473b8e80941Smrg stbrp_node *next = cur->next; 474b8e80941Smrg cur->next = node; 475b8e80941Smrg cur = next; 476b8e80941Smrg } else { 477b8e80941Smrg *res.prev_link = node; 478b8e80941Smrg } 479b8e80941Smrg 480b8e80941Smrg // from here, traverse cur and free the nodes, until we get to one 481b8e80941Smrg // that shouldn't be freed 482b8e80941Smrg while (cur->next && cur->next->x <= res.x + width) { 483b8e80941Smrg stbrp_node *next = cur->next; 484b8e80941Smrg // move the current node to the free list 485b8e80941Smrg cur->next = context->free_head; 486b8e80941Smrg context->free_head = cur; 487b8e80941Smrg cur = next; 488b8e80941Smrg } 489b8e80941Smrg 490b8e80941Smrg // stitch the list back in 491b8e80941Smrg node->next = cur; 492b8e80941Smrg 493b8e80941Smrg if (cur->x < res.x + width) 494b8e80941Smrg cur->x = (stbrp_coord) (res.x + width); 495b8e80941Smrg 496b8e80941Smrg#ifdef _DEBUG 497b8e80941Smrg cur = context->active_head; 498b8e80941Smrg while (cur->x < context->width) { 499b8e80941Smrg STBRP_ASSERT(cur->x < cur->next->x); 500b8e80941Smrg cur = cur->next; 501b8e80941Smrg } 502b8e80941Smrg STBRP_ASSERT(cur->next == NULL); 503b8e80941Smrg 504b8e80941Smrg { 505b8e80941Smrg int count=0; 506b8e80941Smrg cur = context->active_head; 507b8e80941Smrg while (cur) { 508b8e80941Smrg cur = cur->next; 509b8e80941Smrg ++count; 510b8e80941Smrg } 511b8e80941Smrg cur = context->free_head; 512b8e80941Smrg while (cur) { 513b8e80941Smrg cur = cur->next; 514b8e80941Smrg ++count; 515b8e80941Smrg } 516b8e80941Smrg STBRP_ASSERT(count == context->num_nodes+2); 517b8e80941Smrg } 518b8e80941Smrg#endif 519b8e80941Smrg 520b8e80941Smrg return res; 521b8e80941Smrg} 522b8e80941Smrg 523b8e80941Smrg// [DEAR IMGUI] Added STBRP__CDECL 524b8e80941Smrgstatic int STBRP__CDECL rect_height_compare(const void *a, const void *b) 525b8e80941Smrg{ 526b8e80941Smrg const stbrp_rect *p = (const stbrp_rect *) a; 527b8e80941Smrg const stbrp_rect *q = (const stbrp_rect *) b; 528b8e80941Smrg if (p->h > q->h) 529b8e80941Smrg return -1; 530b8e80941Smrg if (p->h < q->h) 531b8e80941Smrg return 1; 532b8e80941Smrg return (p->w > q->w) ? -1 : (p->w < q->w); 533b8e80941Smrg} 534b8e80941Smrg 535b8e80941Smrg// [DEAR IMGUI] Added STBRP__CDECL 536b8e80941Smrgstatic int STBRP__CDECL rect_original_order(const void *a, const void *b) 537b8e80941Smrg{ 538b8e80941Smrg const stbrp_rect *p = (const stbrp_rect *) a; 539b8e80941Smrg const stbrp_rect *q = (const stbrp_rect *) b; 540b8e80941Smrg return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); 541b8e80941Smrg} 542b8e80941Smrg 543b8e80941Smrg#ifdef STBRP_LARGE_RECTS 544b8e80941Smrg#define STBRP__MAXVAL 0xffffffff 545b8e80941Smrg#else 546b8e80941Smrg#define STBRP__MAXVAL 0xffff 547b8e80941Smrg#endif 548b8e80941Smrg 549b8e80941SmrgSTBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) 550b8e80941Smrg{ 551b8e80941Smrg int i, all_rects_packed = 1; 552b8e80941Smrg 553b8e80941Smrg // we use the 'was_packed' field internally to allow sorting/unsorting 554b8e80941Smrg for (i=0; i < num_rects; ++i) { 555b8e80941Smrg rects[i].was_packed = i; 556b8e80941Smrg } 557b8e80941Smrg 558b8e80941Smrg // sort according to heuristic 559b8e80941Smrg STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); 560b8e80941Smrg 561b8e80941Smrg for (i=0; i < num_rects; ++i) { 562b8e80941Smrg if (rects[i].w == 0 || rects[i].h == 0) { 563b8e80941Smrg rects[i].x = rects[i].y = 0; // empty rect needs no space 564b8e80941Smrg } else { 565b8e80941Smrg stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); 566b8e80941Smrg if (fr.prev_link) { 567b8e80941Smrg rects[i].x = (stbrp_coord) fr.x; 568b8e80941Smrg rects[i].y = (stbrp_coord) fr.y; 569b8e80941Smrg } else { 570b8e80941Smrg rects[i].x = rects[i].y = STBRP__MAXVAL; 571b8e80941Smrg } 572b8e80941Smrg } 573b8e80941Smrg } 574b8e80941Smrg 575b8e80941Smrg // unsort 576b8e80941Smrg STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); 577b8e80941Smrg 578b8e80941Smrg // set was_packed flags and all_rects_packed status 579b8e80941Smrg for (i=0; i < num_rects; ++i) { 580b8e80941Smrg rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); 581b8e80941Smrg if (!rects[i].was_packed) 582b8e80941Smrg all_rects_packed = 0; 583b8e80941Smrg } 584b8e80941Smrg 585b8e80941Smrg // return the all_rects_packed status 586b8e80941Smrg return all_rects_packed; 587b8e80941Smrg} 588b8e80941Smrg#endif 589b8e80941Smrg 590b8e80941Smrg/* 591b8e80941Smrg------------------------------------------------------------------------------ 592b8e80941SmrgThis software is available under 2 licenses -- choose whichever you prefer. 593b8e80941Smrg------------------------------------------------------------------------------ 594b8e80941SmrgALTERNATIVE A - MIT License 595b8e80941SmrgCopyright (c) 2017 Sean Barrett 596b8e80941SmrgPermission is hereby granted, free of charge, to any person obtaining a copy of 597b8e80941Smrgthis software and associated documentation files (the "Software"), to deal in 598b8e80941Smrgthe Software without restriction, including without limitation the rights to 599b8e80941Smrguse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 600b8e80941Smrgof the Software, and to permit persons to whom the Software is furnished to do 601b8e80941Smrgso, subject to the following conditions: 602b8e80941SmrgThe above copyright notice and this permission notice shall be included in all 603b8e80941Smrgcopies or substantial portions of the Software. 604b8e80941SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 605b8e80941SmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 606b8e80941SmrgFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 607b8e80941SmrgAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 608b8e80941SmrgLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 609b8e80941SmrgOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 610b8e80941SmrgSOFTWARE. 611b8e80941Smrg------------------------------------------------------------------------------ 612b8e80941SmrgALTERNATIVE B - Public Domain (www.unlicense.org) 613b8e80941SmrgThis is free and unencumbered software released into the public domain. 614b8e80941SmrgAnyone is free to copy, modify, publish, use, compile, sell, or distribute this 615b8e80941Smrgsoftware, either in source code form or as a compiled binary, for any purpose, 616b8e80941Smrgcommercial or non-commercial, and by any means. 617b8e80941SmrgIn jurisdictions that recognize copyright laws, the author or authors of this 618b8e80941Smrgsoftware dedicate any and all copyright interest in the software to the public 619b8e80941Smrgdomain. We make this dedication for the benefit of the public at large and to 620b8e80941Smrgthe detriment of our heirs and successors. We intend this dedication to be an 621b8e80941Smrgovert act of relinquishment in perpetuity of all present and future rights to 622b8e80941Smrgthis software under copyright law. 623b8e80941SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 624b8e80941SmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 625b8e80941SmrgFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 626b8e80941SmrgAUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 627b8e80941SmrgACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 628b8e80941SmrgWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 629b8e80941Smrg------------------------------------------------------------------------------ 630b8e80941Smrg*/ 631