OpenRaider  0.1.4-dev
Open Source Tomb Raider Game Engine implementation
stb_rect_pack.h
Go to the documentation of this file.
1 // stb_rect_pack.h - v0.05 - public domain - rectangle packing
2 // Sean Barrett 2014
3 //
4 // Useful for e.g. packing rectangular textures into an atlas.
5 // Does not do rotation.
6 //
7 // Not necessarily the awesomest packing method, but better than
8 // the totally naive one in stb_truetype (which is primarily what
9 // this is meant to replace).
10 //
11 // Has only had a few tests run, may have issues.
12 //
13 // More docs to come.
14 //
15 // No memory allocations; uses qsort() and assert() from stdlib.
16 //
17 // This library currently uses the Skyline Bottom-Left algorithm.
18 //
19 // Please note: better rectangle packers are welcome! Please
20 // implement them to the same API, but with a different init
21 // function.
22 //
23 // Version history:
24 //
25 // 0.05: added STBRP_ASSERT to allow replacing assert
26 // 0.04: fixed minor bug in STBRP_LARGE_RECTS support
27 // 0.01: initial release
28 
30 //
31 // INCLUDE SECTION
32 //
33 
34 #ifndef STB_INCLUDE_STB_RECT_PACK_H
35 #define STB_INCLUDE_STB_RECT_PACK_H
36 
37 #define STB_RECT_PACK_VERSION 1
38 
39 #ifdef STBRP_STATIC
40 #define STBRP_DEF static
41 #else
42 #define STBRP_DEF extern
43 #endif
44 
45 #ifdef __cplusplus
46 extern "C" {
47 #endif
48 
49 typedef struct stbrp_context stbrp_context;
50 typedef struct stbrp_node stbrp_node;
51 typedef struct stbrp_rect stbrp_rect;
52 
53 #ifdef STBRP_LARGE_RECTS
54 typedef int stbrp_coord;
55 #else
56 typedef unsigned short stbrp_coord;
57 #endif
58 
59 STBRP_DEF void stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
60 // Assign packed locations to rectangles. The rectangles are of type
61 // 'stbrp_rect' defined below, stored in the array 'rects', and there
62 // are 'num_rects' many of them.
63 //
64 // Rectangles which are successfully packed have the 'was_packed' flag
65 // set to a non-zero value and 'x' and 'y' store the minimum location
66 // on each axis (i.e. bottom-left in cartesian coordinates, top-left
67 // if you imagine y increasing downwards). Rectangles which do not fit
68 // have the 'was_packed' flag set to 0.
69 //
70 // You should not try to access the 'rects' array from another thread
71 // while this function is running, as the function temporarily reorders
72 // the array while it executes.
73 //
74 // To pack into another rectangle, you need to call stbrp_init_target
75 // again. To continue packing into the same rectangle, you can call
76 // this function again. Calling this multiple times with multiple rect
77 // arrays will probably produce worse packing results than calling it
78 // a single time with the full rectangle array, but the option is
79 // available.
80 
81 struct stbrp_rect
82 {
83  // reserved for your use:
84  int id;
85 
86  // input:
87  stbrp_coord w, h;
88 
89  // output:
90  stbrp_coord x, y;
91  int was_packed; // non-zero if valid packing
92 
93 }; // 16 bytes, nominally
94 
95 
96 STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
97 // Initialize a rectangle packer to:
98 // pack a rectangle that is 'width' by 'height' in dimensions
99 // using temporary storage provided by the array 'nodes', which is 'num_nodes' long
100 //
101 // You must call this function every time you start packing into a new target.
102 //
103 // There is no "shutdown" function. The 'nodes' memory must stay valid for
104 // the following stbrp_pack_rects() call (or calls), but can be freed after
105 // the call (or calls) finish.
106 //
107 // Note: to guarantee best results, either:
108 // 1. make sure 'num_nodes' >= 'width'
109 // or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
110 //
111 // If you don't do either of the above things, widths will be quantized to multiples
112 // of small integers to guarantee the algorithm doesn't run out of temporary storage.
113 //
114 // If you do #2, then the non-quantized algorithm will be used, but the algorithm
115 // may run out of temporary storage and be unable to pack some rectangles.
116 
117 STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
118 // Optionally call this function after init but before doing any packing to
119 // change the handling of the out-of-temp-memory scenario, described above.
120 // If you call init again, this will be reset to the default (false).
121 
122 
123 STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
124 // Optionally select which packing heuristic the library should use. Different
125 // heuristics will produce better/worse results for different data sets.
126 // If you call init again, this will be reset to the default.
127 
128 enum
129 {
133 };
134 
135 
137 //
138 // the details of the following structures don't matter to you, but they must
139 // be visible so you can handle the memory allocations for them
140 
141 struct stbrp_node
142 {
143  stbrp_coord x,y;
144  stbrp_node *next;
145 };
146 
147 struct stbrp_context
148 {
149  int width;
150  int height;
151  int align;
155  stbrp_node *active_head;
156  stbrp_node *free_head;
157  stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
158 };
159 
160 #ifdef __cplusplus
161 }
162 #endif
163 
164 #endif
165 
167 //
168 // IMPLEMENTATION SECTION
169 //
170 
171 #ifdef STB_RECT_PACK_IMPLEMENTATION
172 #include <stdlib.h>
173 
174 #ifndef STBRP_ASSERT
175 #include <assert.h>
176 #define STBRP_ASSERT assert
177 #endif
178 
179 enum
180 {
181  STBRP__INIT_skyline = 1
182 };
183 
184 STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
185 {
186  switch (context->init_mode) {
187  case STBRP__INIT_skyline:
189  context->heuristic = heuristic;
190  break;
191  default:
192  STBRP_ASSERT(0);
193  }
194 }
195 
196 STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
197 {
198  if (allow_out_of_mem)
199  // if it's ok to run out of memory, then don't bother aligning them;
200  // this gives better packing, but may fail due to OOM (even though
201  // the rectangles easily fit). @TODO a smarter approach would be to only
202  // quantize once we've hit OOM, then we could get rid of this parameter.
203  context->align = 1;
204  else {
205  // if it's not ok to run out of memory, then quantize the widths
206  // so that num_nodes is always enough nodes.
207  //
208  // I.e. num_nodes * align >= width
209  // align >= width / num_nodes
210  // align = ceil(width/num_nodes)
211 
212  context->align = (context->width + context->num_nodes-1) / context->num_nodes;
213  }
214 }
215 
216 STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
217 {
218  int i;
219 #ifndef STBRP_LARGE_RECTS
220  STBRP_ASSERT(width <= 0xffff && height <= 0xffff);
221 #endif
222 
223  for (i=0; i < num_nodes-1; ++i)
224  nodes[i].next = &nodes[i+1];
225  nodes[i].next = NULL;
226  context->init_mode = STBRP__INIT_skyline;
228  context->free_head = &nodes[0];
229  context->active_head = &context->extra[0];
230  context->width = width;
231  context->height = height;
232  context->num_nodes = num_nodes;
233  stbrp_setup_allow_out_of_mem(context, 0);
234 
235  // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
236  context->extra[0].x = 0;
237  context->extra[0].y = 0;
238  context->extra[0].next = &context->extra[1];
239  context->extra[1].x = (stbrp_coord) width;
240 #ifdef STBRP_LARGE_RECTS
241  context->extra[1].y = (1<<30);
242 #else
243  context->extra[1].y = 65535;
244 #endif
245  context->extra[1].next = NULL;
246 }
247 
248 // find minimum y position if it starts at x1
249 static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
250 {
251  (void)c;
252  stbrp_node *node = first;
253  int x1 = x0 + width;
254  int min_y, visited_width, waste_area;
255  STBRP_ASSERT(first->x <= x0);
256 
257  #if 0
258  // skip in case we're past the node
259  while (node->next->x <= x0)
260  ++node;
261  #else
262  STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
263  #endif
264 
265  STBRP_ASSERT(node->x <= x0);
266 
267  min_y = 0;
268  waste_area = 0;
269  visited_width = 0;
270  while (node->x < x1) {
271  if (node->y > min_y) {
272  // raise min_y higher.
273  // we've accounted for all waste up to min_y,
274  // but we'll now add more waste for everything we've visted
275  waste_area += visited_width * (node->y - min_y);
276  min_y = node->y;
277  // the first time through, visited_width might be reduced
278  if (node->x < x0)
279  visited_width += node->next->x - x0;
280  else
281  visited_width += node->next->x - node->x;
282  } else {
283  // add waste area
284  int under_width = node->next->x - node->x;
285  if (under_width + visited_width > width)
286  under_width = width - visited_width;
287  waste_area += under_width * (min_y - node->y);
288  visited_width += under_width;
289  }
290  node = node->next;
291  }
292 
293  *pwaste = waste_area;
294  return min_y;
295 }
296 
297 typedef struct
298 {
299  int x,y;
300  stbrp_node **prev_link;
301 } stbrp__findresult;
302 
303 static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
304 {
305  int best_waste = (1<<30), best_x, best_y = (1 << 30);
306  stbrp__findresult fr;
307  stbrp_node **prev, *node, *tail, **best = NULL;
308 
309  // align to multiple of c->align
310  width = (width + c->align - 1);
311  width -= width % c->align;
312  STBRP_ASSERT(width % c->align == 0);
313 
314  node = c->active_head;
315  prev = &c->active_head;
316  while (node->x + width <= c->width) {
317  int y,waste;
318  y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
319  if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
320  // bottom left
321  if (y < best_y) {
322  best_y = y;
323  best = prev;
324  }
325  } else {
326  // best-fit
327  if (y + height <= c->height) {
328  // can only use it if it first vertically
329  if (y < best_y || (y == best_y && waste < best_waste)) {
330  best_y = y;
331  best_waste = waste;
332  best = prev;
333  }
334  }
335  }
336  prev = &node->next;
337  node = node->next;
338  }
339 
340  best_x = (best == NULL) ? 0 : (*best)->x;
341 
342  // if doing best-fit (BF), we also have to try aligning right edge to each node position
343  //
344  // e.g, if fitting
345  //
346  // ____________________
347  // |____________________|
348  //
349  // into
350  //
351  // | |
352  // | ____________|
353  // |____________|
354  //
355  // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
356  //
357  // This makes BF take about 2x the time
358 
360  tail = c->active_head;
361  node = c->active_head;
362  prev = &c->active_head;
363  // find first node that's admissible
364  while (tail->x < width)
365  tail = tail->next;
366  while (tail) {
367  int xpos = tail->x - width;
368  int y,waste;
369  STBRP_ASSERT(xpos >= 0);
370  // find the left position that matches this
371  while (node->next->x <= xpos) {
372  prev = &node->next;
373  node = node->next;
374  }
375  STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
376  y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
377  if (y + height < c->height) {
378  if (y <= best_y) {
379  if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
380  best_x = xpos;
381  STBRP_ASSERT(y <= best_y);
382  best_y = y;
383  best_waste = waste;
384  best = prev;
385  }
386  }
387  }
388  tail = tail->next;
389  }
390  }
391 
392  fr.prev_link = best;
393  fr.x = best_x;
394  fr.y = best_y;
395  return fr;
396 }
397 
398 static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
399 {
400  // find best position according to heuristic
401  stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
402  stbrp_node *node, *cur;
403 
404  // bail if:
405  // 1. it failed
406  // 2. the best node doesn't fit (we don't always check this)
407  // 3. we're out of memory
408  if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
409  res.prev_link = NULL;
410  return res;
411  }
412 
413  // on success, create new node
414  node = context->free_head;
415  node->x = (stbrp_coord) res.x;
416  node->y = (stbrp_coord) (res.y + height);
417 
418  context->free_head = node->next;
419 
420  // insert the new node into the right starting point, and
421  // let 'cur' point to the remaining nodes needing to be
422  // stiched back in
423 
424  cur = *res.prev_link;
425  if (cur->x < res.x) {
426  // preserve the existing one, so start testing with the next one
427  stbrp_node *next = cur->next;
428  cur->next = node;
429  cur = next;
430  } else {
431  *res.prev_link = node;
432  }
433 
434  // from here, traverse cur and free the nodes, until we get to one
435  // that shouldn't be freed
436  while (cur->next && cur->next->x <= res.x + width) {
437  stbrp_node *next = cur->next;
438  // move the current node to the free list
439  cur->next = context->free_head;
440  context->free_head = cur;
441  cur = next;
442  }
443 
444  // stitch the list back in
445  node->next = cur;
446 
447  if (cur->x < res.x + width)
448  cur->x = (stbrp_coord) (res.x + width);
449 
450 #ifdef _DEBUG
451  cur = context->active_head;
452  while (cur->x < context->width) {
453  STBRP_ASSERT(cur->x < cur->next->x);
454  cur = cur->next;
455  }
456  STBRP_ASSERT(cur->next == NULL);
457 
458  {
459  stbrp_node *L1 = NULL, *L2 = NULL;
460  int count=0;
461  cur = context->active_head;
462  while (cur) {
463  L1 = cur;
464  cur = cur->next;
465  ++count;
466  }
467  cur = context->free_head;
468  while (cur) {
469  L2 = cur;
470  cur = cur->next;
471  ++count;
472  }
473  STBRP_ASSERT(count == context->num_nodes+2);
474  }
475 #endif
476 
477  return res;
478 }
479 
480 static int rect_height_compare(const void *a, const void *b)
481 {
482  stbrp_rect *p = (stbrp_rect *) a;
483  stbrp_rect *q = (stbrp_rect *) b;
484  if (p->h > q->h)
485  return -1;
486  if (p->h < q->h)
487  return 1;
488  return (p->w > q->w) ? -1 : (p->w < q->w);
489 }
490 
491 static int rect_width_compare(const void *a, const void *b)
492 {
493  stbrp_rect *p = (stbrp_rect *) a;
494  stbrp_rect *q = (stbrp_rect *) b;
495  if (p->w > q->w)
496  return -1;
497  if (p->w < q->w)
498  return 1;
499  return (p->h > q->h) ? -1 : (p->h < q->h);
500 }
501 
502 static int rect_original_order(const void *a, const void *b)
503 {
504  stbrp_rect *p = (stbrp_rect *) a;
505  stbrp_rect *q = (stbrp_rect *) b;
506  return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
507 }
508 
509 #ifdef STBRP_LARGE_RECTS
510 #define STBRP__MAXVAL 0xffffffff
511 #else
512 #define STBRP__MAXVAL 0xffff
513 #endif
514 
515 STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
516 {
517  int i;
518 
519  // we use the 'was_packed' field internally to allow sorting/unsorting
520  for (i=0; i < num_rects; ++i) {
521  rects[i].was_packed = i;
522  #ifndef STBRP_LARGE_RECTS
523  STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff);
524  #endif
525  }
526 
527  // sort according to heuristic
528  qsort(rects, num_rects, sizeof(rects[0]), rect_height_compare);
529 
530  for (i=0; i < num_rects; ++i) {
531  stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
532  if (fr.prev_link) {
533  rects[i].x = (stbrp_coord) fr.x;
534  rects[i].y = (stbrp_coord) fr.y;
535  } else {
536  rects[i].x = rects[i].y = STBRP__MAXVAL;
537  }
538  }
539 
540  // unsort
541  qsort(rects, num_rects, sizeof(rects[0]), rect_original_order);
542 
543  // set was_packed flags
544  for (i=0; i < num_rects; ++i)
545  rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
546 }
547 #endif
stbrp_node * free_head
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
stbrp_node extra[2]
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
#define STBRP_ASSERT(x)
Definition: imgui.cpp:379
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
stbrp_node * next
stbrp_coord h
Definition: stb_rect_pack.h:87
stbrp_coord y
Definition: stb_rect_pack.h:90
stbrp_coord x
Definition: stb_rect_pack.h:90
stbrp_coord x
stbrp_coord y
stbrp_coord w
Definition: stb_rect_pack.h:87
unsigned short stbrp_coord
Definition: stb_rect_pack.h:56
#define STBRP_DEF
Definition: stb_rect_pack.h:42
stbrp_node * active_head