stb_rect_pack.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. #include "stb_rect_pack.h"
  2. #define STB_RECT_PACK_IMPLEMENTATION
  3. //////////////////////////////////////////////////////////////////////////////
  4. //
  5. // IMPLEMENTATION SECTION
  6. //
  7. #ifdef STB_RECT_PACK_IMPLEMENTATION
  8. #ifndef STBRP_SORT
  9. #include <stdlib.h>
  10. #define STBRP_SORT qsort
  11. #endif
  12. #ifndef STBRP_ASSERT
  13. #include <assert.h>
  14. #define STBRP_ASSERT assert
  15. #endif
  16. enum
  17. {
  18. STBRP__INIT_skyline = 1,
  19. };
  20. STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
  21. {
  22. switch (context->init_mode) {
  23. case STBRP__INIT_skyline:
  24. STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
  25. context->heuristic = heuristic;
  26. break;
  27. default:
  28. STBRP_ASSERT(0);
  29. }
  30. }
  31. STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
  32. {
  33. if (allow_out_of_mem)
  34. // if it's ok to run out of memory, then don't bother aligning them;
  35. // this gives better packing, but may fail due to OOM (even though
  36. // the rectangles easily fit). @TODO a smarter approach would be to only
  37. // quantize once we've hit OOM, then we could get rid of this parameter.
  38. context->align = 1;
  39. else {
  40. // if it's not ok to run out of memory, then quantize the widths
  41. // so that num_nodes is always enough nodes.
  42. //
  43. // I.e. num_nodes * align >= width
  44. // align >= width / num_nodes
  45. // align = ceil(width/num_nodes)
  46. context->align = (context->width + context->num_nodes-1) / context->num_nodes;
  47. }
  48. }
  49. STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
  50. {
  51. int i;
  52. #ifndef STBRP_LARGE_RECTS
  53. STBRP_ASSERT(width <= 0xffff && height <= 0xffff);
  54. #endif
  55. for (i=0; i < num_nodes-1; ++i)
  56. nodes[i].next = &nodes[i+1];
  57. nodes[i].next = NULL;
  58. context->init_mode = STBRP__INIT_skyline;
  59. context->heuristic = STBRP_HEURISTIC_Skyline_default;
  60. context->free_head = &nodes[0];
  61. context->active_head = &context->extra[0];
  62. context->width = width;
  63. context->height = height;
  64. context->num_nodes = num_nodes;
  65. stbrp_setup_allow_out_of_mem(context, 0);
  66. // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
  67. context->extra[0].x = 0;
  68. context->extra[0].y = 0;
  69. context->extra[0].next = &context->extra[1];
  70. context->extra[1].x = (stbrp_coord) width;
  71. #ifdef STBRP_LARGE_RECTS
  72. context->extra[1].y = (1<<30);
  73. #else
  74. context->extra[1].y = 65535;
  75. #endif
  76. context->extra[1].next = NULL;
  77. }
  78. // find minimum y position if it starts at x1
  79. static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
  80. {
  81. stbrp_node *node = first;
  82. int x1 = x0 + width;
  83. int min_y, visited_width, waste_area;
  84. STBRP_ASSERT(first->x <= x0);
  85. #if 0
  86. // skip in case we're past the node
  87. while (node->next->x <= x0)
  88. ++node;
  89. #else
  90. STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
  91. #endif
  92. STBRP_ASSERT(node->x <= x0);
  93. min_y = 0;
  94. waste_area = 0;
  95. visited_width = 0;
  96. while (node->x < x1) {
  97. if (node->y > min_y) {
  98. // raise min_y higher.
  99. // we've accounted for all waste up to min_y,
  100. // but we'll now add more waste for everything we've visted
  101. waste_area += visited_width * (node->y - min_y);
  102. min_y = node->y;
  103. // the first time through, visited_width might be reduced
  104. if (node->x < x0)
  105. visited_width += node->next->x - x0;
  106. else
  107. visited_width += node->next->x - node->x;
  108. } else {
  109. // add waste area
  110. int under_width = node->next->x - node->x;
  111. if (under_width + visited_width > width)
  112. under_width = width - visited_width;
  113. waste_area += under_width * (min_y - node->y);
  114. visited_width += under_width;
  115. }
  116. node = node->next;
  117. }
  118. *pwaste = waste_area;
  119. return min_y;
  120. }
  121. typedef struct
  122. {
  123. int x,y;
  124. stbrp_node **prev_link;
  125. } stbrp__findresult;
  126. static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
  127. {
  128. int best_waste = (1<<30), best_x, best_y = (1 << 30);
  129. stbrp__findresult fr;
  130. stbrp_node **prev, *node, *tail, **best = NULL;
  131. // align to multiple of c->align
  132. width = (width + c->align - 1);
  133. width -= width % c->align;
  134. STBRP_ASSERT(width % c->align == 0);
  135. node = c->active_head;
  136. prev = &c->active_head;
  137. while (node->x + width <= c->width) {
  138. int y,waste;
  139. y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
  140. if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
  141. // bottom left
  142. if (y < best_y) {
  143. best_y = y;
  144. best = prev;
  145. }
  146. } else {
  147. // best-fit
  148. if (y + height <= c->height) {
  149. // can only use it if it first vertically
  150. if (y < best_y || (y == best_y && waste < best_waste)) {
  151. best_y = y;
  152. best_waste = waste;
  153. best = prev;
  154. }
  155. }
  156. }
  157. prev = &node->next;
  158. node = node->next;
  159. }
  160. best_x = (best == NULL) ? 0 : (*best)->x;
  161. // if doing best-fit (BF), we also have to try aligning right edge to each node position
  162. //
  163. // e.g, if fitting
  164. //
  165. // ____________________
  166. // |____________________|
  167. //
  168. // into
  169. //
  170. // | |
  171. // | ____________|
  172. // |____________|
  173. //
  174. // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
  175. //
  176. // This makes BF take about 2x the time
  177. if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
  178. tail = c->active_head;
  179. node = c->active_head;
  180. prev = &c->active_head;
  181. // find first node that's admissible
  182. while (tail->x < width)
  183. tail = tail->next;
  184. while (tail) {
  185. int xpos = tail->x - width;
  186. int y,waste;
  187. STBRP_ASSERT(xpos >= 0);
  188. // find the left position that matches this
  189. while (node->next->x <= xpos) {
  190. prev = &node->next;
  191. node = node->next;
  192. }
  193. STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
  194. y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
  195. if (y + height < c->height) {
  196. if (y <= best_y) {
  197. if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
  198. best_x = xpos;
  199. STBRP_ASSERT(y <= best_y);
  200. best_y = y;
  201. best_waste = waste;
  202. best = prev;
  203. }
  204. }
  205. }
  206. tail = tail->next;
  207. }
  208. }
  209. fr.prev_link = best;
  210. fr.x = best_x;
  211. fr.y = best_y;
  212. return fr;
  213. }
  214. static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
  215. {
  216. // find best position according to heuristic
  217. stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
  218. stbrp_node *node, *cur;
  219. // bail if:
  220. // 1. it failed
  221. // 2. the best node doesn't fit (we don't always check this)
  222. // 3. we're out of memory
  223. if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
  224. res.prev_link = NULL;
  225. return res;
  226. }
  227. // on success, create new node
  228. node = context->free_head;
  229. node->x = (stbrp_coord) res.x;
  230. node->y = (stbrp_coord) (res.y + height);
  231. context->free_head = node->next;
  232. // insert the new node into the right starting point, and
  233. // let 'cur' point to the remaining nodes needing to be
  234. // stiched back in
  235. cur = *res.prev_link;
  236. if (cur->x < res.x) {
  237. // preserve the existing one, so start testing with the next one
  238. stbrp_node *next = cur->next;
  239. cur->next = node;
  240. cur = next;
  241. } else {
  242. *res.prev_link = node;
  243. }
  244. // from here, traverse cur and free the nodes, until we get to one
  245. // that shouldn't be freed
  246. while (cur->next && cur->next->x <= res.x + width) {
  247. stbrp_node *next = cur->next;
  248. // move the current node to the free list
  249. cur->next = context->free_head;
  250. context->free_head = cur;
  251. cur = next;
  252. }
  253. // stitch the list back in
  254. node->next = cur;
  255. if (cur->x < res.x + width)
  256. cur->x = (stbrp_coord) (res.x + width);
  257. #ifdef _DEBUG
  258. cur = context->active_head;
  259. while (cur->x < context->width) {
  260. STBRP_ASSERT(cur->x < cur->next->x);
  261. cur = cur->next;
  262. }
  263. STBRP_ASSERT(cur->next == NULL);
  264. {
  265. stbrp_node *L1 = NULL, *L2 = NULL;
  266. int count=0;
  267. cur = context->active_head;
  268. while (cur) {
  269. L1 = cur;
  270. cur = cur->next;
  271. ++count;
  272. }
  273. cur = context->free_head;
  274. while (cur) {
  275. L2 = cur;
  276. cur = cur->next;
  277. ++count;
  278. }
  279. STBRP_ASSERT(count == context->num_nodes+2);
  280. }
  281. #endif
  282. return res;
  283. }
  284. static int rect_height_compare(const void *a, const void *b)
  285. {
  286. stbrp_rect *p = (stbrp_rect *) a;
  287. stbrp_rect *q = (stbrp_rect *) b;
  288. if (p->h > q->h)
  289. return -1;
  290. if (p->h < q->h)
  291. return 1;
  292. return (p->w > q->w) ? -1 : (p->w < q->w);
  293. }
  294. static int rect_width_compare(const void *a, const void *b)
  295. {
  296. stbrp_rect *p = (stbrp_rect *) a;
  297. stbrp_rect *q = (stbrp_rect *) b;
  298. if (p->w > q->w)
  299. return -1;
  300. if (p->w < q->w)
  301. return 1;
  302. return (p->h > q->h) ? -1 : (p->h < q->h);
  303. }
  304. static int rect_original_order(const void *a, const void *b)
  305. {
  306. stbrp_rect *p = (stbrp_rect *) a;
  307. stbrp_rect *q = (stbrp_rect *) b;
  308. return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
  309. }
  310. #ifdef STBRP_LARGE_RECTS
  311. #define STBRP__MAXVAL 0xffffffff
  312. #else
  313. #define STBRP__MAXVAL 0xffff
  314. #endif
  315. STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
  316. {
  317. int i;
  318. // we use the 'was_packed' field internally to allow sorting/unsorting
  319. for (i=0; i < num_rects; ++i) {
  320. rects[i].was_packed = i;
  321. #ifndef STBRP_LARGE_RECTS
  322. STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff);
  323. #endif
  324. }
  325. // sort according to heuristic
  326. STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
  327. for (i=0; i < num_rects; ++i) {
  328. if (rects[i].w == 0 || rects[i].h == 0) {
  329. rects[i].x = rects[i].y = 0; // empty rect needs no space
  330. } else {
  331. stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
  332. if (fr.prev_link) {
  333. rects[i].x = (stbrp_coord) fr.x;
  334. rects[i].y = (stbrp_coord) fr.y;
  335. } else {
  336. rects[i].x = rects[i].y = STBRP__MAXVAL;
  337. }
  338. }
  339. }
  340. // unsort
  341. STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
  342. // set was_packed flags
  343. for (i=0; i < num_rects; ++i)
  344. rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
  345. }
  346. #endif