1 /* stbhw - v0.6 - http://nothings.org/gamedev/herringbone
2 Herringbone Wang Tile Generator - Sean Barrett 2014 - public domain
4 == LICENSE ==============================
6 This software is dual-licensed to the public domain and under the following
7 license: you are granted a perpetual, irrevocable license to copy, modify,
8 publish, and distribute this file as you see fit.
10 == WHAT IT IS ===========================
12 This library is an SDK for Herringbone Wang Tile generation:
14 http://nothings.org/gamedev/herringbone
16 The core design is that you use this library offline to generate a
17 "template" of the tiles you'll create. You then edit those tiles, then
18 load the created tile image file back into this library and use it at
19 runtime to generate "maps".
21 You cannot load arbitrary tile image files with this library; it is
22 only designed to load image files made from the template it created.
23 It stores a binary description of the tile sizes & constraints in a
24 few pixels, and uses those to recover the rules, rather than trying
25 to parse the tiles themselves.
27 You *can* use this library to generate from arbitrary tile sets, but
28 only by loading the tile set and specifying the constraints explicitly
31 == COMPILING ============================
33 1. #define STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION before including this
34 header file in *one* source file to create the implementation
37 2. optionally #define STB_HBWANG_RAND() to be a random number
38 generator. if you don't define it, it will use rand(),
39 and you need to seed srand() yourself.
41 3. optionally #define STB_HBWANG_ASSERT(x), otherwise
44 4. optionally #define STB_HBWANG_STATIC to force all symbols to be
45 static instead of public, so they are only accesible
46 in the source file that creates the implementation
48 5. optionally #define STB_HBWANG_NO_REPITITION_REDUCTION to disable
49 the code that tries to reduce having the same tile appear
50 adjacent to itself in wang-corner-tile mode (e.g. imagine
51 if you were doing something where 90% of things should be
52 the same grass tile, you need to disable this system)
54 6. optionally define STB_HBWANG_MAX_X and STB_HBWANG_MAX_Y
55 to be the max dimensions of the generated map in multiples
56 of the wang tile's short side's length (e.g. if you
57 have 20x10 wang tiles, so short_side_len=10, and you
58 have MAX_X is 17, then the largest map you can generate
59 is 170 pixels wide). The defaults are 100x100. This
60 is used to define static arrays which affect memory
63 == USING ================================
65 To use the map generator, you need a tileset. You can download
66 some sample tilesets from http://nothings.org/gamedev/herringbone
68 Then see the "sample application" below.
70 You can also use this file to generate templates for
71 tilesets which you then hand-edit to create the data.
74 == MEMORY MANAGEMENT ====================
76 The tileset loader allocates memory with malloc(). The map
77 generator does no memory allocation, so e.g. you can load
78 tilesets at startup and never free them and never do any
82 == SAMPLE APPLICATION ===================
88 #define STB_IMAGE_IMPLEMENTATION
89 #include "stb_image.h" // http://nothings.org/stb_image.c
91 #define STB_IMAGE_WRITE_IMPLEMENTATION
92 #include "stb_image_write.h" // http://nothings.org/stb/stb_image_write.h
94 #define STB_HBWANG_IMPLEMENTATION
95 #include "stb_hbwang.h"
97 int main(int argc, char **argv)
104 fprintf(stderr, "Usage: mapgen {tile-file} {xsize} {ysize}\n"
105 "generates file named 'test_map.png'\n");
108 data = stbi_load(argv[1], &w, &h, NULL, 3);
112 fprintf(stderr, "Error opening or parsing '%s' as an image file\n", argv[1]);
115 if (xs < 1 || xs > 1000) {
116 fprintf(stderr, "xsize invalid or out of range\n");
119 if (ys < 1 || ys > 1000) {
120 fprintf(stderr, "ysize invalid or out of range\n");
124 stbhw_build_tileset_from_image(&ts, data, w*3, w, h);
127 // allocate a buffer to create the final image to
128 data = malloc(3 * xs * ys);
131 stbhw_generate_image(&ts, NULL, data, xs*3, xs, ys);
133 stbi_write_png("test_map.png", xs, ys, 3, data, xs*3);
135 stbhw_free_tileset(&ts);
141 == VERSION HISTORY ===================
143 0.6 2014-08-17 - fix broken map-maker
144 0.5 2014-07-07 - initial release
148 //////////////////////////////////////////////////////////////////////////////
150 // HEADER FILE SECTION //
153 #ifndef INCLUDE_STB_HWANG_H
154 #define INCLUDE_STB_HWANG_H
156 #ifdef STB_HBWANG_STATIC
157 #define STBHW_EXTERN static
160 #define STBHW_EXTERN extern "C"
162 #define STBHW_EXTERN extern
166 typedef struct stbhw_tileset stbhw_tileset
;
168 // returns description of last error produced by any function (not thread-safe)
169 STBHW_EXTERN
char *stbhw_get_last_error(void);
171 // build a tileset from an image that conforms to a template created by this
172 // library. (you allocate storage for stbhw_tileset and function fills it out;
173 // memory for individual tiles are malloc()ed).
174 // returns non-zero on success, 0 on error
175 STBHW_EXTERN
int stbhw_build_tileset_from_image(stbhw_tileset
*ts
,
176 unsigned char *pixels
, int stride_in_bytes
, int w
, int h
);
178 // free a tileset built by stbhw_build_tileset_from_image
179 STBHW_EXTERN
void stbhw_free_tileset(stbhw_tileset
*ts
);
181 // generate a map that is w * h pixels (3-bytes each)
182 // returns non-zero on success, 0 on error
183 // not thread-safe (uses a global data structure to avoid memory management)
184 // weighting should be NULL, as non-NULL weighting is currently untested
185 STBHW_EXTERN
int stbhw_generate_image(stbhw_tileset
*ts
, int **weighting
,
186 unsigned char *pixels
, int stride_in_bytes
, int w
, int h
);
188 //////////////////////////////////////
190 // TILESET DATA STRUCTURE
192 // if you use the image-to-tileset system from this file, you
193 // don't need to worry about these data structures. but if you
194 // want to build/load a tileset yourself, you'll need to fill
199 // the edge or vertex constraints, according to diagram below
200 signed char a
,b
,c
,d
,e
,f
;
202 // The herringbone wang tile data; it is a bitmap which is either
203 // w=2*short_sidelen,h=short_sidelen, or w=short_sidelen,h=2*short_sidelen.
204 // it is always RGB, stored row-major, with no padding between rows.
205 // (allocate stbhw_tile structure to be large enough for the pixel data)
206 unsigned char pixels
[1];
212 int num_color
[6]; // number of colors for each of 6 edge types or 4 corner types
214 stbhw_tile
**h_tiles
;
215 stbhw_tile
**v_tiles
;
216 int num_h_tiles
, max_h_tiles
;
217 int num_v_tiles
, max_v_tiles
;
220 /////////////// TEMPLATE GENERATOR //////////////////////////
222 // when requesting a template, you fill out this data
225 int is_corner
; // using corner colors or edge colors?
226 int short_side_len
; // rectangles is 2n x n, n = short_side_len
227 int num_color
[6]; // see below diagram for meaning of the index to this;
228 // 6 values if edge (!is_corner), 4 values if is_corner
229 // legal numbers: 1..8 if edge, 1..4 if is_corner
230 int num_vary_x
; // additional number of variations along x axis in the template
231 int num_vary_y
; // additional number of variations along y axis in the template
232 int corner_type_color_template
[4][4];
233 // if corner_type_color_template[s][t] is non-zero, then any
234 // corner of type s generated as color t will get a little
235 // corner sample markup in the template image data
239 // computes the size needed for the template image
240 STBHW_EXTERN
void stbhw_get_template_size(stbhw_config
*c
, int *w
, int *h
);
242 // generates a template image, assuming data is 3*w*h bytes long, RGB format
243 STBHW_EXTERN
int stbhw_make_template(stbhw_config
*c
, unsigned char *data
, int w
, int h
, int stride_in_bytes
);
245 #endif//INCLUDE_STB_HWANG_H
248 // TILE CONSTRAINT TYPES
250 // there are 4 "types" of corners and 6 types of edges.
251 // you can configure the tileset to have different numbers
252 // of colors for each type of color or edge.
256 // 0---*---1---*---2---*---3
260 // 1---*---2---*---3 0---*---1---*---2
264 // 0---*---1---*---2---*---3
269 // *---2---*---3---* *---0---*
273 // *---0---*---2---* * *
281 // each corner/edge has a color; this shows the name
282 // of the variable containing the color
284 // corner constraints:
290 // a---*---b---*---c b e
294 // d---*---e---*---f c---*---f
299 // *---a---*---b---* *---a---*
303 // *---e---*---f---* * *
311 //////////////////////////////////////////////////////////////////////////////
313 // IMPLEMENTATION SECTION //
316 #ifdef STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION
319 #include <string.h> // memcpy
320 #include <stdlib.h> // malloc
322 #ifndef STB_HBWANG_RAND
324 #define STB_HBWANG_RAND() (rand() >> 4)
327 #ifndef STB_HBWANG_ASSERT
329 #define STB_HBWANG_ASSERT(x) assert(x)
333 #ifndef STB_HBWANG_MAX_X
334 #define STB_HBWANG_MAX_X 100
337 #ifndef STB_HBWANG_MAX_Y
338 #define STB_HBWANG_MAX_Y 100
341 // global variables for color assignments
342 // @MEMORY change these to just store last two/three rows
343 // and keep them on the stack
344 static signed char c_color
[STB_HBWANG_MAX_Y
+6][STB_HBWANG_MAX_X
+6];
345 static signed char v_color
[STB_HBWANG_MAX_Y
+6][STB_HBWANG_MAX_X
+5];
346 static signed char h_color
[STB_HBWANG_MAX_Y
+5][STB_HBWANG_MAX_X
+6];
348 static char *stbhw_error
;
349 STBHW_EXTERN
char *stbhw_get_last_error(void)
351 char *temp
= stbhw_error
;
359 /////////////////////////////////////////////////////////////
361 // SHARED TEMPLATE-DESCRIPTION CODE
363 // Used by both template generator and tileset parser; by
364 // using the same code, they are locked in sync and we don't
365 // need to try to do more sophisticated parsing of edge color
366 // markup or something.
368 typedef void stbhw__process_rect(struct stbhw__process
*p
, int xpos
, int ypos
,
369 int a
, int b
, int c
, int d
, int e
, int f
);
371 typedef struct stbhw__process
375 stbhw__process_rect
*process_h_rect
;
376 stbhw__process_rect
*process_v_rect
;
381 static void stbhw__process_h_row(stbhw__process
*p
,
393 for (v
=0; v
< variants
; ++v
)
394 for (f
=f0
; f
<= f1
; ++f
)
395 for (e
=e0
; e
<= e1
; ++e
)
396 for (d
=d0
; d
<= d1
; ++d
)
397 for (c
=c0
; c
<= c1
; ++c
)
398 for (b
=b0
; b
<= b1
; ++b
)
399 for (a
=a0
; a
<= a1
; ++a
) {
400 p
->process_h_rect(p
, xpos
, ypos
, a
,b
,c
,d
,e
,f
);
401 xpos
+= 2*p
->c
->short_side_len
+ 3;
405 static void stbhw__process_v_row(stbhw__process
*p
,
417 for (v
=0; v
< variants
; ++v
)
418 for (f
=f0
; f
<= f1
; ++f
)
419 for (e
=e0
; e
<= e1
; ++e
)
420 for (d
=d0
; d
<= d1
; ++d
)
421 for (c
=c0
; c
<= c1
; ++c
)
422 for (b
=b0
; b
<= b1
; ++b
)
423 for (a
=a0
; a
<= a1
; ++a
) {
424 p
->process_v_rect(p
, xpos
, ypos
, a
,b
,c
,d
,e
,f
);
425 xpos
+= p
->c
->short_side_len
+3;
429 static void stbhw__get_template_info(stbhw_config
*c
, int *w
, int *h
, int *h_count
, int *v_count
)
432 int horz_count
,vert_count
;
435 int horz_w
= c
->num_color
[1] * c
->num_color
[2] * c
->num_color
[3] * c
->num_vary_x
;
436 int horz_h
= c
->num_color
[0] * c
->num_color
[1] * c
->num_color
[2] * c
->num_vary_y
;
438 int vert_w
= c
->num_color
[0] * c
->num_color
[3] * c
->num_color
[2] * c
->num_vary_y
;
439 int vert_h
= c
->num_color
[1] * c
->num_color
[0] * c
->num_color
[3] * c
->num_vary_x
;
441 int horz_x
= horz_w
* (2*c
->short_side_len
+ 3);
442 int horz_y
= horz_h
* ( c
->short_side_len
+ 3);
444 int vert_x
= vert_w
* ( c
->short_side_len
+ 3);
445 int vert_y
= vert_h
* (2*c
->short_side_len
+ 3);
447 horz_count
= horz_w
* horz_h
;
448 vert_count
= vert_w
* vert_h
;
450 size_x
= horz_x
> vert_x
? horz_x
: vert_x
;
451 size_y
= 2 + horz_y
+ 2 + vert_y
;
453 int horz_w
= c
->num_color
[0] * c
->num_color
[1] * c
->num_color
[2] * c
->num_vary_x
;
454 int horz_h
= c
->num_color
[3] * c
->num_color
[4] * c
->num_color
[2] * c
->num_vary_y
;
456 int vert_w
= c
->num_color
[0] * c
->num_color
[5] * c
->num_color
[1] * c
->num_vary_y
;
457 int vert_h
= c
->num_color
[3] * c
->num_color
[4] * c
->num_color
[5] * c
->num_vary_x
;
459 int horz_x
= horz_w
* (2*c
->short_side_len
+ 3);
460 int horz_y
= horz_h
* ( c
->short_side_len
+ 3);
462 int vert_x
= vert_w
* ( c
->short_side_len
+ 3);
463 int vert_y
= vert_h
* (2*c
->short_side_len
+ 3);
465 horz_count
= horz_w
* horz_h
;
466 vert_count
= vert_w
* vert_h
;
468 size_x
= horz_x
> vert_x
? horz_x
: vert_x
;
469 size_y
= 2 + horz_y
+ 2 + vert_y
;
473 if (h_count
) *h_count
= horz_count
;
474 if (v_count
) *v_count
= vert_count
;
477 STBHW_EXTERN
void stbhw_get_template_size(stbhw_config
*c
, int *w
, int *h
)
479 stbhw__get_template_info(c
, w
, h
, NULL
, NULL
);
482 static int stbhw__process_template(stbhw__process
*p
)
486 stbhw_config
*c
= p
->c
;
488 stbhw__get_template_info(c
, &size_x
, &size_y
, NULL
, NULL
);
490 if (p
->w
< size_x
|| p
->h
< size_y
) {
491 stbhw_error
= "image too small for configuration";
497 for (k
=0; k
< c
->num_color
[2]; ++k
) {
498 for (j
=0; j
< c
->num_color
[1]; ++j
) {
499 for (i
=0; i
< c
->num_color
[0]; ++i
) {
500 for (q
=0; q
< c
->num_vary_y
; ++q
) {
501 stbhw__process_h_row(p
, 0,ypos
,
502 0,c
->num_color
[1]-1, 0,c
->num_color
[2]-1, 0,c
->num_color
[3]-1,
505 ypos
+= c
->short_side_len
+ 3;
511 for (k
=0; k
< c
->num_color
[3]; ++k
) {
512 for (j
=0; j
< c
->num_color
[0]; ++j
) {
513 for (i
=0; i
< c
->num_color
[1]; ++i
) {
514 for (q
=0; q
< c
->num_vary_x
; ++q
) {
515 stbhw__process_v_row(p
, 0,ypos
,
516 0,c
->num_color
[0]-1, 0,c
->num_color
[3]-1, 0,c
->num_color
[2]-1,
519 ypos
+= (c
->short_side_len
*2) + 3;
524 assert(ypos
== size_y
);
527 for (k
=0; k
< c
->num_color
[3]; ++k
) {
528 for (j
=0; j
< c
->num_color
[4]; ++j
) {
529 for (i
=0; i
< c
->num_color
[2]; ++i
) {
530 for (q
=0; q
< c
->num_vary_y
; ++q
) {
531 stbhw__process_h_row(p
, 0,ypos
,
532 0,c
->num_color
[2]-1, k
,k
,
533 0,c
->num_color
[1]-1, j
,j
,
534 0,c
->num_color
[0]-1, i
,i
,
536 ypos
+= c
->short_side_len
+ 3;
542 for (k
=0; k
< c
->num_color
[3]; ++k
) {
543 for (j
=0; j
< c
->num_color
[4]; ++j
) {
544 for (i
=0; i
< c
->num_color
[5]; ++i
) {
545 for (q
=0; q
< c
->num_vary_x
; ++q
) {
546 stbhw__process_v_row(p
, 0,ypos
,
547 0,c
->num_color
[0]-1, i
,i
,
548 0,c
->num_color
[1]-1, j
,j
,
549 0,c
->num_color
[5]-1, k
,k
,
551 ypos
+= (c
->short_side_len
*2) + 3;
556 assert(ypos
== size_y
);
562 /////////////////////////////////////////////////////////////
567 static void stbhw__draw_pixel(unsigned char *output
, int stride
, int x
, int y
, unsigned char c
[3])
569 memcpy(output
+ y
*stride
+ x
*3, c
, 3);
572 static void stbhw__draw_h_tile(unsigned char *output
, int stride
, int xmax
, int ymax
, int x
, int y
, stbhw_tile
*h
, int sz
)
575 for (j
=0; j
< sz
; ++j
)
576 if (y
+j
>= 0 && y
+j
< ymax
)
577 for (i
=0; i
< sz
*2; ++i
)
578 if (x
+i
>= 0 && x
+i
< xmax
)
579 stbhw__draw_pixel(output
,stride
, x
+i
,y
+j
, &h
->pixels
[(j
*sz
*2 + i
)*3]);
582 static void stbhw__draw_v_tile(unsigned char *output
, int stride
, int xmax
, int ymax
, int x
, int y
, stbhw_tile
*h
, int sz
)
585 for (j
=0; j
< sz
*2; ++j
)
586 if (y
+j
>= 0 && y
+j
< ymax
)
587 for (i
=0; i
< sz
; ++i
)
588 if (x
+i
>= 0 && x
+i
< xmax
)
589 stbhw__draw_pixel(output
,stride
, x
+i
,y
+j
, &h
->pixels
[(j
*sz
+ i
)*3]);
593 // randomly choose a tile that fits constraints for a given spot, and update the constraints
594 static stbhw_tile
* stbhw__choose_tile(stbhw_tile
**list
, int numlist
,
595 signed char *a
, signed char *b
, signed char *c
,
596 signed char *d
, signed char *e
, signed char *f
,
599 int i
,n
,m
= 1<<30,pass
;
600 for (pass
=0; pass
< 2; ++pass
) {
603 // count number of variants that match this partial set of constraints
605 // stop on randomly selected match
606 for (i
=0; i
< numlist
; ++i
) {
607 stbhw_tile
*h
= list
[i
];
608 if ((*a
< 0 || *a
== h
->a
) &&
609 (*b
< 0 || *b
== h
->b
) &&
610 (*c
< 0 || *c
== h
->c
) &&
611 (*d
< 0 || *d
== h
->d
) &&
612 (*e
< 0 || *e
== h
->e
) &&
613 (*f
< 0 || *f
== h
->f
)) {
615 n
+= weighting
[0][i
];
620 // update constraints to reflect what we placed
632 stbhw_error
= "couldn't find tile matching constraints";
635 m
= STB_HBWANG_RAND() % n
;
637 STB_HBWANG_ASSERT(0);
641 static int stbhw__match(int x
, int y
)
643 return c_color
[y
][x
] == c_color
[y
+1][x
+1];
646 static int stbhw__weighted(int num_options
, int *weights
)
648 int k
, total
, choice
;
650 for (k
=0; k
< num_options
; ++k
)
652 choice
= STB_HBWANG_RAND() % total
;
654 for (k
=0; k
< num_options
; ++k
) {
659 STB_HBWANG_ASSERT(k
< num_options
);
663 static int stbhw__change_color(int old_color
, int num_options
, int *weights
)
666 int k
, total
, choice
;
668 for (k
=0; k
< num_options
; ++k
)
671 choice
= STB_HBWANG_RAND() % total
;
673 for (k
=0; k
< num_options
; ++k
) {
674 if (k
!= old_color
) {
680 STB_HBWANG_ASSERT(k
< num_options
);
683 int offset
= 1+STB_HBWANG_RAND() % (num_options
-1);
684 return (old_color
+offset
) % num_options
;
690 // generate a map that is w * h pixels (3-bytes each)
691 // returns 1 on success, 0 on error
692 STBHW_EXTERN
int stbhw_generate_image(stbhw_tileset
*ts
, int **weighting
, unsigned char *output
, int stride
, int w
, int h
)
694 int sidelen
= ts
->short_side_len
;
695 int xmax
= (w
/ sidelen
) + 6;
696 int ymax
= (h
/ sidelen
) + 6;
697 if (xmax
> STB_HBWANG_MAX_X
+6 || ymax
> STB_HBWANG_MAX_Y
+6) {
698 stbhw_error
= "increase STB_HBWANG_MAX_X/Y";
704 int *cc
= ts
->num_color
;
706 for (j
=0; j
< ymax
; ++j
) {
707 for (i
=0; i
< xmax
; ++i
) {
708 int p
= (i
-j
+1)&3; // corner type
709 if (weighting
==NULL
|| weighting
[p
]==0 || cc
[p
] == 1)
710 c_color
[j
][i
] = STB_HBWANG_RAND() % cc
[p
];
712 c_color
[j
][i
] = stbhw__weighted(cc
[p
], weighting
[p
]);
715 #ifndef STB_HBWANG_NO_REPITITION_REDUCTION
716 // now go back through and make sure we don't have adjancent 3x2 vertices that are identical,
717 // to avoid really obvious repetition (which happens easily with extreme weights)
718 for (j
=0; j
< ymax
-3; ++j
) {
719 for (i
=0; i
< xmax
-3; ++i
) {
720 int p
= (i
-j
+1) & 3; // corner type
721 STB_HBWANG_ASSERT(i
+3 < STB_HBWANG_MAX_X
+6);
722 STB_HBWANG_ASSERT(j
+3 < STB_HBWANG_MAX_Y
+6);
723 if (stbhw__match(i
,j
) && stbhw__match(i
,j
+1) && stbhw__match(i
,j
+2)
724 && stbhw__match(i
+1,j
) && stbhw__match(i
+1,j
+1) && stbhw__match(i
+1,j
+2)) {
725 int p
= ((i
+1)-(j
+1)+1) & 3;
727 c_color
[j
+1][i
+1] = stbhw__change_color(c_color
[j
+1][i
+1], cc
[p
], weighting
? weighting
[p
] : NULL
);
729 if (stbhw__match(i
,j
) && stbhw__match(i
+1,j
) && stbhw__match(i
+2,j
)
730 && stbhw__match(i
,j
+1) && stbhw__match(i
+1,j
+1) && stbhw__match(i
+2,j
+1)) {
731 int p
= ((i
+2)-(j
+1)+1) & 3;
733 c_color
[j
+1][i
+2] = stbhw__change_color(c_color
[j
+1][i
+2], cc
[p
], weighting
? weighting
[p
] : NULL
);
740 for (j
= -1; ypos
< h
; ++j
) {
741 // a general herringbone row consists of:
742 // horizontal left block, the bottom of a previous vertical, the top of a new vertical
744 // displace horizontally according to pattern
751 int xpos
= i
* sidelen
;
754 // horizontal left-block
755 if (xpos
+ sidelen
*2 >= 0 && ypos
>= 0) {
756 stbhw_tile
*t
= stbhw__choose_tile(
757 ts
->h_tiles
, ts
->num_h_tiles
,
758 &c_color
[j
+2][i
+2], &c_color
[j
+2][i
+3], &c_color
[j
+2][i
+4],
759 &c_color
[j
+3][i
+2], &c_color
[j
+3][i
+3], &c_color
[j
+3][i
+4],
764 stbhw__draw_h_tile(output
,stride
,w
,h
, xpos
, ypos
, t
, sidelen
);
767 // now we're at the end of a previous vertical one
769 // now we're at the start of a new vertical one
771 stbhw_tile
*t
= stbhw__choose_tile(
772 ts
->v_tiles
, ts
->num_v_tiles
,
773 &c_color
[j
+2][i
+5], &c_color
[j
+3][i
+5], &c_color
[j
+4][i
+5],
774 &c_color
[j
+2][i
+6], &c_color
[j
+3][i
+6], &c_color
[j
+4][i
+6],
779 stbhw__draw_v_tile(output
,stride
,w
,h
, xpos
, ypos
, t
, sidelen
);
785 // @TODO edge-color repetition reduction
787 memset(v_color
, -1, sizeof(v_color
));
788 memset(h_color
, -1, sizeof(h_color
));
791 for (j
= -1; ypos
<h
; ++j
) {
792 // a general herringbone row consists of:
793 // horizontal left block, the bottom of a previous vertical, the top of a new vertical
795 // displace horizontally according to pattern
802 int xpos
= i
* sidelen
;
805 // horizontal left-block
806 if (xpos
+ sidelen
*2 >= 0 && ypos
>= 0) {
807 stbhw_tile
*t
= stbhw__choose_tile(
808 ts
->h_tiles
, ts
->num_h_tiles
,
809 &h_color
[j
+2][i
+2], &h_color
[j
+2][i
+3],
810 &v_color
[j
+2][i
+2], &v_color
[j
+2][i
+4],
811 &h_color
[j
+3][i
+2], &h_color
[j
+3][i
+3],
814 if (t
== NULL
) return 0;
815 stbhw__draw_h_tile(output
,stride
,w
,h
, xpos
, ypos
, t
, sidelen
);
818 // now we're at the end of a previous vertical one
820 // now we're at the start of a new vertical one
822 stbhw_tile
*t
= stbhw__choose_tile(
823 ts
->v_tiles
, ts
->num_v_tiles
,
825 &v_color
[j
+2][i
+5], &v_color
[j
+2][i
+6],
826 &v_color
[j
+3][i
+5], &v_color
[j
+3][i
+6],
830 if (t
== NULL
) return 0;
831 stbhw__draw_v_tile(output
,stride
,w
,h
, xpos
, ypos
, t
, sidelen
);
840 static void stbhw__parse_h_rect(stbhw__process
*p
, int xpos
, int ypos
,
841 int a
, int b
, int c
, int d
, int e
, int f
)
843 int len
= p
->c
->short_side_len
;
844 stbhw_tile
*h
= (stbhw_tile
*) malloc(sizeof(*h
)-1 + 3 * (len
*2) * len
);
848 h
->a
= a
, h
->b
= b
, h
->c
= c
, h
->d
= d
, h
->e
= e
, h
->f
= f
;
849 for (j
=0; j
< len
; ++j
)
850 for (i
=0; i
< len
*2; ++i
)
851 memcpy(h
->pixels
+ j
*(3*len
*2) + i
*3, p
->data
+(ypos
+j
)*p
->stride
+(xpos
+i
)*3, 3);
852 STB_HBWANG_ASSERT(p
->ts
->num_h_tiles
< p
->ts
->max_h_tiles
);
853 p
->ts
->h_tiles
[p
->ts
->num_h_tiles
++] = h
;
856 static void stbhw__parse_v_rect(stbhw__process
*p
, int xpos
, int ypos
,
857 int a
, int b
, int c
, int d
, int e
, int f
)
859 int len
= p
->c
->short_side_len
;
860 stbhw_tile
*h
= (stbhw_tile
*) malloc(sizeof(*h
)-1 + 3 * (len
*2) * len
);
864 h
->a
= a
, h
->b
= b
, h
->c
= c
, h
->d
= d
, h
->e
= e
, h
->f
= f
;
865 for (j
=0; j
< len
*2; ++j
)
866 for (i
=0; i
< len
; ++i
)
867 memcpy(h
->pixels
+ j
*(3*len
) + i
*3, p
->data
+(ypos
+j
)*p
->stride
+(xpos
+i
)*3, 3);
868 STB_HBWANG_ASSERT(p
->ts
->num_v_tiles
< p
->ts
->max_v_tiles
);
869 p
->ts
->v_tiles
[p
->ts
->num_v_tiles
++] = h
;
872 STBHW_EXTERN
int stbhw_build_tileset_from_image(stbhw_tileset
*ts
, unsigned char *data
, int stride
, int w
, int h
)
874 int i
, h_count
, v_count
;
875 unsigned char header
[9];
876 stbhw_config c
= { 0 };
877 stbhw__process p
= { 0 };
879 // extract binary header
881 // remove encoding that makes it more visually obvious it encodes actual data
882 for (i
=0; i
< 9; ++i
)
883 header
[i
] = data
[w
*3 - 1 - i
] ^ (i
*55);
885 // extract header info
886 if (header
[7] == 0xc0) {
889 for (i
=0; i
< 4; ++i
)
890 c
.num_color
[i
] = header
[i
];
891 c
.num_vary_x
= header
[4];
892 c
.num_vary_y
= header
[5];
893 c
.short_side_len
= header
[6];
897 for (i
=0; i
< 6; ++i
)
898 c
.num_color
[i
] = header
[i
];
899 c
.num_vary_x
= header
[6];
900 c
.num_vary_y
= header
[7];
901 c
.short_side_len
= header
[8];
904 if (c
.num_vary_x
< 0 || c
.num_vary_x
> 64 || c
.num_vary_y
< 0 || c
.num_vary_y
> 64)
906 if (c
.short_side_len
== 0)
908 if (c
.num_color
[0] > 32 || c
.num_color
[1] > 32 || c
.num_color
[2] > 32 || c
.num_color
[3] > 32)
911 stbhw__get_template_info(&c
, NULL
, NULL
, &h_count
, &v_count
);
913 ts
->is_corner
= c
.is_corner
;
914 ts
->short_side_len
= c
.short_side_len
;
915 memcpy(ts
->num_color
, c
.num_color
, sizeof(ts
->num_color
));
917 ts
->max_h_tiles
= h_count
;
918 ts
->max_v_tiles
= v_count
;
920 ts
->num_h_tiles
= ts
->num_v_tiles
= 0;
922 ts
->h_tiles
= (stbhw_tile
**) malloc(sizeof(*ts
->h_tiles
) * h_count
);
923 ts
->v_tiles
= (stbhw_tile
**) malloc(sizeof(*ts
->v_tiles
) * v_count
);
928 p
.process_h_rect
= stbhw__parse_h_rect
;
929 p
.process_v_rect
= stbhw__parse_v_rect
;
934 // load all the tiles out of the image
935 return stbhw__process_template(&p
);
938 STBHW_EXTERN
void stbhw_free_tileset(stbhw_tileset
*ts
)
941 for (i
=0; i
< ts
->num_h_tiles
; ++i
)
942 free(ts
->h_tiles
[i
]);
943 for (i
=0; i
< ts
->num_v_tiles
; ++i
)
944 free(ts
->v_tiles
[i
]);
949 ts
->num_h_tiles
= ts
->max_h_tiles
= 0;
950 ts
->num_v_tiles
= ts
->max_v_tiles
= 0;
953 //////////////////////////////////////////////////////////////////////////////
962 static void stbhw__set_pixel(unsigned char *data
, int stride
, int xpos
, int ypos
, unsigned char color
[3])
964 memcpy(data
+ ypos
*stride
+ xpos
*3, color
, 3);
967 static void stbhw__stbhw__set_pixel_whiten(unsigned char *data
, int stride
, int xpos
, int ypos
, unsigned char color
[3])
971 for (i
=0; i
< 3; ++i
)
972 c2
[i
] = (color
[i
]*2 + 255)/3;
973 memcpy(data
+ ypos
*stride
+ xpos
*3, c2
, 3);
977 static unsigned char stbhw__black
[3] = { 0,0,0 };
979 // each edge set gets its own unique color variants
980 // used http://phrogz.net/css/distinct-colors.html to generate this set,
981 // but it's not very good and needs to be revised
983 static unsigned char stbhw__color
[7][8][3] =
985 { {255,51,51} , {143,143,29}, {0,199,199}, {159,119,199}, {0,149,199} , {143, 0,143}, {255,128,0}, {64,255,0}, },
986 { {235,255,30 }, {255,0,255}, {199,139,119}, {29,143, 57}, {143,0,71} , { 0,143,143}, {0,99,199}, {143,71,0}, },
987 { {0,149,199} , {143, 0,143}, {255,128,0}, {64,255,0}, {255,191,0} , {51,255,153}, {0,0,143}, {199,119,159},},
988 { {143,0,71} , { 0,143,143}, {0,99,199}, {143,71,0}, {255,190,153}, { 0,255,255}, {128,0,255}, {255,51,102},},
989 { {255,191,0} , {51,255,153}, {0,0,143}, {199,119,159}, {255,51,51} , {143,143,29}, {0,199,199}, {159,119,199},},
990 { {255,190,153}, { 0,255,255}, {128,0,255}, {255,51,102}, {235,255,30 }, {255,0,255}, {199,139,119}, {29,143, 57}, },
992 { {40,40,40 }, { 90,90,90 }, { 150,150,150 }, { 200,200,200 },
993 { 255,90,90 }, { 160,160,80}, { 50,150,150 }, { 200,50,200 } },
996 static void stbhw__draw_hline(unsigned char *data
, int stride
, int xpos
, int ypos
, int color
, int len
, int slot
)
999 int j
= len
* 6 / 16;
1000 int k
= len
* 10 / 16;
1001 for (i
=0; i
< len
; ++i
)
1002 stbhw__set_pixel(data
, stride
, xpos
+i
, ypos
, stbhw__black
);
1009 for (i
=j
; i
< k
; ++i
)
1010 stbhw__stbhw__set_pixel_whiten(data
, stride
, xpos
+i
, ypos
, stbhw__color
[slot
][color
]);
1013 static void stbhw__draw_vline(unsigned char *data
, int stride
, int xpos
, int ypos
, int color
, int len
, int slot
)
1016 int j
= len
* 6 / 16;
1017 int k
= len
* 10 / 16;
1018 for (i
=0; i
< len
; ++i
)
1019 stbhw__set_pixel(data
, stride
, xpos
, ypos
+i
, stbhw__black
);
1026 for (i
=j
; i
< k
; ++i
)
1027 stbhw__stbhw__set_pixel_whiten(data
, stride
, xpos
, ypos
+i
, stbhw__color
[slot
][color
]);
1030 // 0--*--1--*--2--*--3
1034 // 1--*--2--*--3 0--*--1--*--2
1038 // 0--*--1--*--2--*--3
1040 // variables while enumerating (no correspondence between corners
1041 // of the types is implied by these variables)
1043 // a-----b-----c a-----d
1047 // d-----e-----f b e
1054 unsigned char stbhw__corner_colors
[4][4][3] =
1056 { { 255,0,0 }, { 200,200,200 }, { 100,100,200 }, { 255,200,150 }, },
1057 { { 0,0,255 }, { 255,255,0 }, { 100,200,100 }, { 150,255,200 }, },
1058 { { 255,0,255 }, { 80,80,80 }, { 200,100,100 }, { 200,150,255 }, },
1059 { { 0,255,255 }, { 0,255,0 }, { 200,120,200 }, { 255,200,200 }, },
1062 int stbhw__corner_colors_to_edge_color
[4][4] =
1065 { 0, 1, 4, 9, }, // 0
1066 { 2, 3, 5, 10, }, // 1
1067 { 6, 7, 8, 11, }, // 2
1068 { 12, 13, 14, 15, }, // 3
1071 #define stbhw__c2e stbhw__corner_colors_to_edge_color
1073 static void stbhw__draw_clipped_corner(unsigned char *data
, int stride
, int xpos
, int ypos
, int w
, int h
, int x
, int y
)
1075 static unsigned char template_color
[3] = { 167,204,204 };
1077 for (j
= -2; j
<= 1; ++j
) {
1078 for (i
= -2; i
<= 1; ++i
) {
1079 if ((i
== -2 || i
== 1) && (j
== -2 || j
== 1))
1082 if (x
+i
< 1 || x
+i
> w
) continue;
1083 if (y
+j
< 1 || y
+j
> h
) continue;
1084 stbhw__set_pixel(data
, stride
, xpos
+x
+i
, ypos
+y
+j
, template_color
);
1091 static void stbhw__edge_process_h_rect(stbhw__process
*p
, int xpos
, int ypos
,
1092 int a
, int b
, int c
, int d
, int e
, int f
)
1094 int len
= p
->c
->short_side_len
;
1095 stbhw__draw_hline(p
->data
, p
->stride
, xpos
+1 , ypos
, a
, len
, 2);
1096 stbhw__draw_hline(p
->data
, p
->stride
, xpos
+ len
+1 , ypos
, b
, len
, 3);
1097 stbhw__draw_vline(p
->data
, p
->stride
, xpos
, ypos
+1 , c
, len
, 1);
1098 stbhw__draw_vline(p
->data
, p
->stride
, xpos
+2*len
+1 , ypos
+1 , d
, len
, 4);
1099 stbhw__draw_hline(p
->data
, p
->stride
, xpos
+1 , ypos
+ len
+1, e
, len
, 0);
1100 stbhw__draw_hline(p
->data
, p
->stride
, xpos
+ len
+1 , ypos
+ len
+1, f
, len
, 2);
1103 static void stbhw__edge_process_v_rect(stbhw__process
*p
, int xpos
, int ypos
,
1104 int a
, int b
, int c
, int d
, int e
, int f
)
1106 int len
= p
->c
->short_side_len
;
1107 stbhw__draw_hline(p
->data
, p
->stride
, xpos
+1 , ypos
, a
, len
, 0);
1108 stbhw__draw_vline(p
->data
, p
->stride
, xpos
, ypos
+1 , b
, len
, 5);
1109 stbhw__draw_vline(p
->data
, p
->stride
, xpos
+ len
+1, ypos
+1 , c
, len
, 1);
1110 stbhw__draw_vline(p
->data
, p
->stride
, xpos
, ypos
+ len
+1, d
, len
, 4);
1111 stbhw__draw_vline(p
->data
, p
->stride
, xpos
+ len
+1, ypos
+ len
+1, e
, len
, 5);
1112 stbhw__draw_hline(p
->data
, p
->stride
, xpos
+1 , ypos
+ 2*len
+1, f
, len
, 3);
1115 static void stbhw__corner_process_h_rect(stbhw__process
*p
, int xpos
, int ypos
,
1116 int a
, int b
, int c
, int d
, int e
, int f
)
1118 int len
= p
->c
->short_side_len
;
1120 stbhw__draw_hline(p
->data
, p
->stride
, xpos
+1 , ypos
, stbhw__c2e
[a
][b
], len
, 2);
1121 stbhw__draw_hline(p
->data
, p
->stride
, xpos
+ len
+1 , ypos
, stbhw__c2e
[b
][c
], len
, 3);
1122 stbhw__draw_vline(p
->data
, p
->stride
, xpos
, ypos
+1 , stbhw__c2e
[a
][d
], len
, 1);
1123 stbhw__draw_vline(p
->data
, p
->stride
, xpos
+2*len
+1 , ypos
+1 , stbhw__c2e
[c
][f
], len
, 4);
1124 stbhw__draw_hline(p
->data
, p
->stride
, xpos
+1 , ypos
+ len
+1, stbhw__c2e
[d
][e
], len
, 0);
1125 stbhw__draw_hline(p
->data
, p
->stride
, xpos
+ len
+1 , ypos
+ len
+1, stbhw__c2e
[e
][f
], len
, 2);
1127 if (p
->c
->corner_type_color_template
[1][a
]) stbhw__draw_clipped_corner(p
->data
,p
->stride
, xpos
,ypos
, len
*2,len
, 1,1);
1128 if (p
->c
->corner_type_color_template
[2][b
]) stbhw__draw_clipped_corner(p
->data
,p
->stride
, xpos
,ypos
, len
*2,len
, len
+1,1);
1129 if (p
->c
->corner_type_color_template
[3][c
]) stbhw__draw_clipped_corner(p
->data
,p
->stride
, xpos
,ypos
, len
*2,len
, len
*2+1,1);
1131 if (p
->c
->corner_type_color_template
[0][d
]) stbhw__draw_clipped_corner(p
->data
,p
->stride
, xpos
,ypos
, len
*2,len
, 1,len
+1);
1132 if (p
->c
->corner_type_color_template
[1][e
]) stbhw__draw_clipped_corner(p
->data
,p
->stride
, xpos
,ypos
, len
*2,len
, len
+1,len
+1);
1133 if (p
->c
->corner_type_color_template
[2][f
]) stbhw__draw_clipped_corner(p
->data
,p
->stride
, xpos
,ypos
, len
*2,len
, len
*2+1,len
+1);
1135 stbhw__set_pixel(p
->data
, p
->stride
, xpos
, ypos
, stbhw__corner_colors
[1][a
]);
1136 stbhw__set_pixel(p
->data
, p
->stride
, xpos
+len
, ypos
, stbhw__corner_colors
[2][b
]);
1137 stbhw__set_pixel(p
->data
, p
->stride
, xpos
+2*len
+1, ypos
, stbhw__corner_colors
[3][c
]);
1138 stbhw__set_pixel(p
->data
, p
->stride
, xpos
, ypos
+len
+1, stbhw__corner_colors
[0][d
]);
1139 stbhw__set_pixel(p
->data
, p
->stride
, xpos
+len
, ypos
+len
+1, stbhw__corner_colors
[1][e
]);
1140 stbhw__set_pixel(p
->data
, p
->stride
, xpos
+2*len
+1, ypos
+len
+1, stbhw__corner_colors
[2][f
]);
1143 static void stbhw__corner_process_v_rect(stbhw__process
*p
, int xpos
, int ypos
,
1144 int a
, int b
, int c
, int d
, int e
, int f
)
1146 int len
= p
->c
->short_side_len
;
1148 stbhw__draw_hline(p
->data
, p
->stride
, xpos
+1 , ypos
, stbhw__c2e
[a
][d
], len
, 0);
1149 stbhw__draw_vline(p
->data
, p
->stride
, xpos
, ypos
+1 , stbhw__c2e
[a
][b
], len
, 5);
1150 stbhw__draw_vline(p
->data
, p
->stride
, xpos
+ len
+1, ypos
+1 , stbhw__c2e
[d
][e
], len
, 1);
1151 stbhw__draw_vline(p
->data
, p
->stride
, xpos
, ypos
+ len
+1, stbhw__c2e
[b
][c
], len
, 4);
1152 stbhw__draw_vline(p
->data
, p
->stride
, xpos
+ len
+1, ypos
+ len
+1, stbhw__c2e
[e
][f
], len
, 5);
1153 stbhw__draw_hline(p
->data
, p
->stride
, xpos
+1 , ypos
+ 2*len
+1, stbhw__c2e
[c
][f
], len
, 3);
1155 if (p
->c
->corner_type_color_template
[0][a
]) stbhw__draw_clipped_corner(p
->data
,p
->stride
, xpos
,ypos
, len
,len
*2, 1,1);
1156 if (p
->c
->corner_type_color_template
[3][b
]) stbhw__draw_clipped_corner(p
->data
,p
->stride
, xpos
,ypos
, len
,len
*2, 1,len
+1);
1157 if (p
->c
->corner_type_color_template
[2][c
]) stbhw__draw_clipped_corner(p
->data
,p
->stride
, xpos
,ypos
, len
,len
*2, 1,len
*2+1);
1159 if (p
->c
->corner_type_color_template
[1][d
]) stbhw__draw_clipped_corner(p
->data
,p
->stride
, xpos
,ypos
, len
,len
*2, len
+1,1);
1160 if (p
->c
->corner_type_color_template
[0][e
]) stbhw__draw_clipped_corner(p
->data
,p
->stride
, xpos
,ypos
, len
,len
*2, len
+1,len
+1);
1161 if (p
->c
->corner_type_color_template
[3][f
]) stbhw__draw_clipped_corner(p
->data
,p
->stride
, xpos
,ypos
, len
,len
*2, len
+1,len
*2+1);
1163 stbhw__set_pixel(p
->data
, p
->stride
, xpos
, ypos
, stbhw__corner_colors
[0][a
]);
1164 stbhw__set_pixel(p
->data
, p
->stride
, xpos
, ypos
+len
, stbhw__corner_colors
[3][b
]);
1165 stbhw__set_pixel(p
->data
, p
->stride
, xpos
, ypos
+2*len
+1, stbhw__corner_colors
[2][c
]);
1166 stbhw__set_pixel(p
->data
, p
->stride
, xpos
+len
+1, ypos
, stbhw__corner_colors
[1][d
]);
1167 stbhw__set_pixel(p
->data
, p
->stride
, xpos
+len
+1, ypos
+len
, stbhw__corner_colors
[0][e
]);
1168 stbhw__set_pixel(p
->data
, p
->stride
, xpos
+len
+1, ypos
+2*len
+1, stbhw__corner_colors
[3][f
]);
1171 // generates a template image, assuming data is 3*w*h bytes long, RGB format
1172 STBHW_EXTERN
int stbhw_make_template(stbhw_config
*c
, unsigned char *data
, int w
, int h
, int stride_in_bytes
)
1180 p
.stride
= stride_in_bytes
;
1185 p
.process_h_rect
= stbhw__corner_process_h_rect
;
1186 p
.process_v_rect
= stbhw__corner_process_v_rect
;
1188 p
.process_h_rect
= stbhw__edge_process_h_rect
;
1189 p
.process_v_rect
= stbhw__edge_process_v_rect
;
1192 for (i
=0; i
< p
.h
; ++i
)
1193 memset(p
.data
+ i
*p
.stride
, 255, 3*p
.w
);
1195 if (!stbhw__process_template(&p
))
1199 // write out binary information in first line of image
1200 for (i
=0; i
< 4; ++i
)
1201 data
[w
*3-1-i
] = c
->num_color
[i
];
1202 data
[w
*3-1-i
] = c
->num_vary_x
;
1203 data
[w
*3-2-i
] = c
->num_vary_y
;
1204 data
[w
*3-3-i
] = c
->short_side_len
;
1205 data
[w
*3-4-i
] = 0xc0;
1207 for (i
=0; i
< 6; ++i
)
1208 data
[w
*3-1-i
] = c
->num_color
[i
];
1209 data
[w
*3-1-i
] = c
->num_vary_x
;
1210 data
[w
*3-2-i
] = c
->num_vary_y
;
1211 data
[w
*3-3-i
] = c
->short_side_len
;
1214 // make it more obvious it encodes actual data
1215 for (i
=0; i
< 9; ++i
)
1216 p
.data
[p
.w
*3 - 1 - i
] ^= i
*55;
1220 #endif // STB_HBWANG_IMPLEMENTATION