1 /* stbhw - v0.6 -  http://nothings.org/gamedev/herringbone
2    Herringbone Wang Tile Generator - Sean Barrett 2014 - public domain
3 
4 == LICENSE ==============================
5 
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.
9 
10 == WHAT IT IS ===========================
11 
12  This library is an SDK for Herringbone Wang Tile generation:
13 
14       http://nothings.org/gamedev/herringbone
15 
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".
20 
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.
26 
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
29  yourself.
30 
31 == COMPILING ============================
32 
33  1. #define STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION before including this
34     header file in *one* source file to create the implementation
35     in that source file.
36 
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.
40 
41  3. optionally #define STB_HBWANG_ASSERT(x), otherwise
42     it will use assert()
43 
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
47 
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)
53 
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
61     usage.
62 
63 == USING ================================
64 
65   To use the map generator, you need a tileset. You can download
66   some sample tilesets from http://nothings.org/gamedev/herringbone
67 
68   Then see the "sample application" below.
69 
70   You can also use this file to generate templates for
71   tilesets which you then hand-edit to create the data.
72 
73 
74 == MEMORY MANAGEMENT ====================
75 
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
79   further allocation.
80 
81 
82 == SAMPLE APPLICATION ===================
83 
84 #include <stdlib.h>
85 #include <stdio.h>
86 #include <time.h>
87 
88 #define STB_IMAGE_IMPLEMENTATION
89 #include "stb_image.h"        // http://nothings.org/stb_image.c
90 
91 #define STB_IMAGE_WRITE_IMPLEMENTATION
92 #include "stb_image_write.h"  // http://nothings.org/stb/stb_image_write.h
93 
94 #define STB_HBWANG_IMPLEMENTATION
95 #include "stb_hbwang.h"
96 
97 int main(int argc, char **argv)
98 {
99    unsigned char *data;
100    int xs,ys, w,h;
101    stbhw_tileset ts;
102 
103    if (argc != 4) {
104       fprintf(stderr, "Usage: mapgen {tile-file} {xsize} {ysize}\n"
105                       "generates file named 'test_map.png'\n");
106       exit(1);
107    }
108    data = stbi_load(argv[1], &w, &h, NULL, 3);
109    xs = atoi(argv[2]);
110    ys = atoi(argv[3]);
111    if (data == NULL) {
112       fprintf(stderr, "Error opening or parsing '%s' as an image file\n", argv[1]);
113       exit(1);
114    }
115    if (xs < 1 || xs > 1000) {
116       fprintf(stderr, "xsize invalid or out of range\n");
117       exit(1);
118    }
119    if (ys < 1 || ys > 1000) {
120       fprintf(stderr, "ysize invalid or out of range\n");
121       exit(1);
122    }
123 
124    stbhw_build_tileset_from_image(&ts, data, w*3, w, h);
125    free(data);
126 
127    // allocate a buffer to create the final image to
128    data = malloc(3 * xs * ys);
129 
130    srand(time(NULL));
131    stbhw_generate_image(&ts, NULL, data, xs*3, xs, ys);
132 
133    stbi_write_png("test_map.png", xs, ys, 3, data, xs*3);
134 
135    stbhw_free_tileset(&ts);
136    free(data);
137 
138    return 0;
139 }
140 
141 == VERSION HISTORY ===================
142 
143 	0.6   2014-08-17   - fix broken map-maker
144 	0.5   2014-07-07   - initial release
145 
146 */
147 
148 //////////////////////////////////////////////////////////////////////////////
149 //                                                                          //
150 //                         HEADER FILE SECTION                              //
151 //                                                                          //
152 
153 #ifndef INCLUDE_STB_HWANG_H
154 #define INCLUDE_STB_HWANG_H
155 
156 #ifdef STB_HBWANG_STATIC
157 #define STBHW_EXTERN static
158 #else
159 #ifdef __cplusplus
160 #define STBHW_EXTERN extern "C"
161 #else
162 #define STBHW_EXTERN extern
163 #endif
164 #endif
165 
166 typedef struct stbhw_tileset stbhw_tileset;
167 
168 // returns description of last error produced by any function (not thread-safe)
169 STBHW_EXTERN char *stbhw_get_last_error(void);
170 
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);
177 
178 // free a tileset built by stbhw_build_tileset_from_image
179 STBHW_EXTERN void stbhw_free_tileset(stbhw_tileset *ts);
180 
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);
187 
188 //////////////////////////////////////
189 //
190 // TILESET DATA STRUCTURE
191 //
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
195 // these out.
196 
197 typedef struct
198 {
199    // the edge or vertex constraints, according to diagram below
200    signed char a,b,c,d,e,f;
201 
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];
207 } stbhw_tile;
208 
209 struct stbhw_tileset
210 {
211    int is_corner;
212    int num_color[6];  // number of colors for each of 6 edge types or 4 corner types
213    int short_side_len;
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;
218 };
219 
220 ///////////////  TEMPLATE GENERATOR  //////////////////////////
221 
222 // when requesting a template, you fill out this data
223 typedef struct
224 {
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
236 
237 } stbhw_config;
238 
239 // computes the size needed for the template image
240 STBHW_EXTERN void stbhw_get_template_size(stbhw_config *c, int *w, int *h);
241 
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);
244 
245 #endif//INCLUDE_STB_HWANG_H
246 
247 
248 // TILE CONSTRAINT TYPES
249 //
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.
253 //
254 // corner types:
255 //
256 //                     0---*---1---*---2---*---3
257 //                     |       |               |
258 //                     *       *               *
259 //                     |       |               |
260 //     1---*---2---*---3       0---*---1---*---2
261 //     |               |       |
262 //     *               *       *
263 //     |               |       |
264 //     0---*---1---*---2---*---3
265 //
266 //
267 //  edge types:
268 //
269 //     *---2---*---3---*      *---0---*
270 //     |               |      |       |
271 //     1               4      5       1
272 //     |               |      |       |
273 //     *---0---*---2---*      *       *
274 //                            |       |
275 //                            4       5
276 //                            |       |
277 //                            *---3---*
278 //
279 // TILE CONSTRAINTS
280 //
281 // each corner/edge has a color; this shows the name
282 // of the variable containing the color
283 //
284 // corner constraints:
285 //
286 //                        a---*---d
287 //                        |       |
288 //                        *       *
289 //                        |       |
290 //     a---*---b---*---c  b       e
291 //     |               |  |       |
292 //     *               *  *       *
293 //     |               |  |       |
294 //     d---*---e---*---f  c---*---f
295 //
296 //
297 //  edge constraints:
298 //
299 //     *---a---*---b---*      *---a---*
300 //     |               |      |       |
301 //     c               d      b       c
302 //     |               |      |       |
303 //     *---e---*---f---*      *       *
304 //                            |       |
305 //                            d       e
306 //                            |       |
307 //                            *---f---*
308 //
309 
310 
311 //////////////////////////////////////////////////////////////////////////////
312 //                                                                          //
313 //                       IMPLEMENTATION SECTION                             //
314 //                                                                          //
315 
316 #ifdef STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION
317 
318 
319 #include <string.h> // memcpy
320 #include <stdlib.h> // malloc
321 
322 #ifndef STB_HBWANG_RAND
323 #include <stdlib.h>
324 #define STB_HBWANG_RAND()  (rand() >> 4)
325 #endif
326 
327 #ifndef STB_HBWANG_ASSERT
328 #include <assert.h>
329 #define STB_HBWANG_ASSERT(x)  assert(x)
330 #endif
331 
332 // map size
333 #ifndef STB_HBWANG_MAX_X
334 #define STB_HBWANG_MAX_X  100
335 #endif
336 
337 #ifndef STB_HBWANG_MAX_Y
338 #define STB_HBWANG_MAX_Y  100
339 #endif
340 
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];
347 
348 static char *stbhw_error;
stbhw_get_last_error(void)349 STBHW_EXTERN char *stbhw_get_last_error(void)
350 {
351    char *temp = stbhw_error;
352    stbhw_error = 0;
353    return temp;
354 }
355 
356 
357 
358 
359 /////////////////////////////////////////////////////////////
360 //
361 //  SHARED TEMPLATE-DESCRIPTION CODE
362 //
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.
367 
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);
370 
371 typedef struct stbhw__process
372 {
373    stbhw_tileset *ts;
374    stbhw_config *c;
375    stbhw__process_rect *process_h_rect;
376    stbhw__process_rect *process_v_rect;
377    unsigned char *data;
378    int stride,w,h;
379 } stbhw__process;
380 
stbhw__process_h_row(stbhw__process * p,int xpos,int ypos,int a0,int a1,int b0,int b1,int c0,int c1,int d0,int d1,int e0,int e1,int f0,int f1,int variants)381 static void stbhw__process_h_row(stbhw__process *p,
382                            int xpos, int ypos,
383                            int a0, int a1,
384                            int b0, int b1,
385                            int c0, int c1,
386                            int d0, int d1,
387                            int e0, int e1,
388                            int f0, int f1,
389                            int variants)
390 {
391    int a,b,c,d,e,f,v;
392 
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;
402                      }
403 }
404 
stbhw__process_v_row(stbhw__process * p,int xpos,int ypos,int a0,int a1,int b0,int b1,int c0,int c1,int d0,int d1,int e0,int e1,int f0,int f1,int variants)405 static void stbhw__process_v_row(stbhw__process *p,
406                            int xpos, int ypos,
407                            int a0, int a1,
408                            int b0, int b1,
409                            int c0, int c1,
410                            int d0, int d1,
411                            int e0, int e1,
412                            int f0, int f1,
413                            int variants)
414 {
415    int a,b,c,d,e,f,v;
416 
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;
426                      }
427 }
428 
stbhw__get_template_info(stbhw_config * c,int * w,int * h,int * h_count,int * v_count)429 static void stbhw__get_template_info(stbhw_config *c, int *w, int *h, int *h_count, int *v_count)
430 {
431    int size_x,size_y;
432    int horz_count,vert_count;
433 
434    if (c->is_corner) {
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;
437 
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;
440 
441       int horz_x = horz_w * (2*c->short_side_len + 3);
442       int horz_y = horz_h * (  c->short_side_len + 3);
443 
444       int vert_x = vert_w * (  c->short_side_len + 3);
445       int vert_y = vert_h * (2*c->short_side_len + 3);
446 
447       horz_count = horz_w * horz_h;
448       vert_count = vert_w * vert_h;
449 
450       size_x = horz_x > vert_x ? horz_x : vert_x;
451       size_y = 2 + horz_y + 2 + vert_y;
452    } else {
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;
455 
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;
458 
459       int horz_x = horz_w * (2*c->short_side_len + 3);
460       int horz_y = horz_h * (  c->short_side_len + 3);
461 
462       int vert_x = vert_w * (  c->short_side_len + 3);
463       int vert_y = vert_h * (2*c->short_side_len + 3);
464 
465       horz_count = horz_w * horz_h;
466       vert_count = vert_w * vert_h;
467 
468       size_x = horz_x > vert_x ? horz_x : vert_x;
469       size_y = 2 + horz_y + 2 + vert_y;
470    }
471    if (w) *w = size_x;
472    if (h) *h = size_y;
473    if (h_count) *h_count = horz_count;
474    if (v_count) *v_count = vert_count;
475 }
476 
stbhw_get_template_size(stbhw_config * c,int * w,int * h)477 STBHW_EXTERN void stbhw_get_template_size(stbhw_config *c, int *w, int *h)
478 {
479    stbhw__get_template_info(c, w, h, NULL, NULL);
480 }
481 
stbhw__process_template(stbhw__process * p)482 static int stbhw__process_template(stbhw__process *p)
483 {
484    int i,j,k,q, ypos;
485    int size_x, size_y;
486    stbhw_config *c = p->c;
487 
488    stbhw__get_template_info(c, &size_x, &size_y, NULL, NULL);
489 
490    if (p->w < size_x || p->h < size_y) {
491       stbhw_error = "image too small for configuration";
492       return 0;
493    }
494 
495    if (c->is_corner) {
496       ypos = 2;
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,
503                      i,i, j,j, k,k,
504                      c->num_vary_x);
505                   ypos += c->short_side_len + 3;
506                }
507             }
508          }
509       }
510       ypos += 2;
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,
517                      i,i, j,j, k,k,
518                      c->num_vary_y);
519                   ypos += (c->short_side_len*2) + 3;
520                }
521             }
522          }
523       }
524       assert(ypos == size_y);
525    } else {
526       ypos = 2;
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,
535                      c->num_vary_x);
536                   ypos += c->short_side_len + 3;
537                }
538             }
539          }
540       }
541       ypos += 2;
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,
550                      c->num_vary_y);
551                   ypos += (c->short_side_len*2) + 3;
552                }
553             }
554          }
555       }
556       assert(ypos == size_y);
557    }
558    return 1;
559 }
560 
561 
562 /////////////////////////////////////////////////////////////
563 //
564 //  MAP GENERATOR
565 //
566 
stbhw__draw_pixel(unsigned char * output,int stride,int x,int y,unsigned char c[3])567 static void stbhw__draw_pixel(unsigned char *output, int stride, int x, int y, unsigned char c[3])
568 {
569    memcpy(output + y*stride + x*3, c, 3);
570 }
571 
stbhw__draw_h_tile(unsigned char * output,int stride,int xmax,int ymax,int x,int y,stbhw_tile * h,int sz)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)
573 {
574    int i,j;
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]);
580 }
581 
stbhw__draw_v_tile(unsigned char * output,int stride,int xmax,int ymax,int x,int y,stbhw_tile * h,int sz)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)
583 {
584    int i,j;
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]);
590 }
591 
592 
593 // randomly choose a tile that fits constraints for a given spot, and update the constraints
stbhw__choose_tile(stbhw_tile ** list,int numlist,signed char * a,signed char * b,signed char * c,signed char * d,signed char * e,signed char * f,int ** weighting)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,
597                                       int **weighting)
598 {
599    int i,n,m = 1<<30,pass;
600    for (pass=0; pass < 2; ++pass) {
601       n=0;
602       // pass #1:
603       //   count number of variants that match this partial set of constraints
604       // pass #2:
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)) {
614             if (weighting)
615                n += weighting[0][i];
616             else
617                n += 1;
618             if (n > m) {
619                // use list[i]
620                // update constraints to reflect what we placed
621                *a = h->a;
622                *b = h->b;
623                *c = h->c;
624                *d = h->d;
625                *e = h->e;
626                *f = h->f;
627                return h;
628             }
629          }
630       }
631       if (n == 0) {
632          stbhw_error = "couldn't find tile matching constraints";
633          return NULL;
634       }
635       m = STB_HBWANG_RAND() % n;
636    }
637    STB_HBWANG_ASSERT(0);
638    return NULL;
639 }
640 
stbhw__match(int x,int y)641 static int stbhw__match(int x, int y)
642 {
643    return c_color[y][x] == c_color[y+1][x+1];
644 }
645 
stbhw__weighted(int num_options,int * weights)646 static int stbhw__weighted(int num_options, int *weights)
647 {
648    int k, total, choice;
649    total = 0;
650    for (k=0; k < num_options; ++k)
651       total += weights[k];
652    choice = STB_HBWANG_RAND() % total;
653    total = 0;
654    for (k=0; k < num_options; ++k) {
655       total += weights[k];
656       if (choice < total)
657          break;
658    }
659    STB_HBWANG_ASSERT(k < num_options);
660    return k;
661 }
662 
stbhw__change_color(int old_color,int num_options,int * weights)663 static int stbhw__change_color(int old_color, int num_options, int *weights)
664 {
665    if (weights) {
666       int k, total, choice;
667       total = 0;
668       for (k=0; k < num_options; ++k)
669          if (k != old_color)
670             total += weights[k];
671       choice = STB_HBWANG_RAND() % total;
672       total = 0;
673       for (k=0; k < num_options; ++k) {
674          if (k != old_color) {
675             total += weights[k];
676             if (choice < total)
677                break;
678          }
679       }
680       STB_HBWANG_ASSERT(k < num_options);
681       return k;
682    } else {
683       int offset = 1+STB_HBWANG_RAND() % (num_options-1);
684       return (old_color+offset) % num_options;
685    }
686 }
687 
688 
689 
690 // generate a map that is w * h pixels (3-bytes each)
691 // returns 1 on success, 0 on error
stbhw_generate_image(stbhw_tileset * ts,int ** weighting,unsigned char * output,int stride,int w,int h)692 STBHW_EXTERN int stbhw_generate_image(stbhw_tileset *ts, int **weighting, unsigned char *output, int stride, int w, int h)
693 {
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";
699       return 0;
700    }
701 
702    if (ts->is_corner) {
703       int i,j, ypos;
704       int *cc = ts->num_color;
705 
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];
711             else
712                c_color[j][i] = stbhw__weighted(cc[p], weighting[p]);
713          }
714       }
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;
726                if (cc[p] > 1)
727                   c_color[j+1][i+1] = stbhw__change_color(c_color[j+1][i+1], cc[p], weighting ? weighting[p] : NULL);
728             }
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;
732                if (cc[p] > 1)
733                   c_color[j+1][i+2] = stbhw__change_color(c_color[j+1][i+2], cc[p], weighting ? weighting[p] : NULL);
734             }
735          }
736       }
737       #endif
738 
739       ypos = -1 * sidelen;
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
743          int phase = (j & 3);
744          // displace horizontally according to pattern
745          if (phase == 0) {
746             i = 0;
747          } else {
748             i = phase-4;
749          }
750          for (i;; i += 4) {
751             int xpos = i * sidelen;
752             if (xpos >= w)
753                break;
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],
760                   weighting
761                );
762                if (t == NULL)
763                   return 0;
764                stbhw__draw_h_tile(output,stride,w,h, xpos, ypos, t, sidelen);
765             }
766             xpos += sidelen * 2;
767             // now we're at the end of a previous vertical one
768             xpos += sidelen;
769             // now we're at the start of a new vertical one
770             if (xpos < w) {
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],
775                   weighting
776                );
777                if (t == NULL)
778                   return 0;
779                stbhw__draw_v_tile(output,stride,w,h, xpos, ypos,  t, sidelen);
780             }
781          }
782          ypos += sidelen;
783       }
784    } else {
785       // @TODO edge-color repetition reduction
786       int i,j, ypos;
787       memset(v_color, -1, sizeof(v_color));
788       memset(h_color, -1, sizeof(h_color));
789 
790       ypos = -1 * sidelen;
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
794          int phase = (j & 3);
795          // displace horizontally according to pattern
796          if (phase == 0) {
797             i = 0;
798          } else {
799             i = phase-4;
800          }
801          for (i;; i += 4) {
802             int xpos = i * sidelen;
803             if (xpos >= w)
804                break;
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],
812                   weighting
813                );
814                if (t == NULL) return 0;
815                stbhw__draw_h_tile(output,stride,w,h, xpos, ypos, t, sidelen);
816             }
817             xpos += sidelen * 2;
818             // now we're at the end of a previous vertical one
819             xpos += sidelen;
820             // now we're at the start of a new vertical one
821             if (xpos < w) {
822                stbhw_tile *t = stbhw__choose_tile(
823                   ts->v_tiles, ts->num_v_tiles,
824                   &h_color[j+2][i+5],
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],
827                   &h_color[j+4][i+5],
828                   weighting
829                );
830                if (t == NULL) return 0;
831                stbhw__draw_v_tile(output,stride,w,h, xpos, ypos,  t, sidelen);
832             }
833          }
834          ypos += sidelen;
835       }
836    }
837    return 1;
838 }
839 
stbhw__parse_h_rect(stbhw__process * p,int xpos,int ypos,int a,int b,int c,int d,int e,int f)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)
842 {
843    int len = p->c->short_side_len;
844    stbhw_tile *h = (stbhw_tile *) malloc(sizeof(*h)-1 + 3 * (len*2) * len);
845    int i,j;
846    ++xpos;
847    ++ypos;
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;
854 }
855 
stbhw__parse_v_rect(stbhw__process * p,int xpos,int ypos,int a,int b,int c,int d,int e,int f)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)
858 {
859    int len = p->c->short_side_len;
860    stbhw_tile *h = (stbhw_tile *) malloc(sizeof(*h)-1 + 3 * (len*2) * len);
861    int i,j;
862    ++xpos;
863    ++ypos;
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;
870 }
871 
stbhw_build_tileset_from_image(stbhw_tileset * ts,unsigned char * data,int stride,int w,int h)872 STBHW_EXTERN int stbhw_build_tileset_from_image(stbhw_tileset *ts, unsigned char *data, int stride, int w, int h)
873 {
874    int i, h_count, v_count;
875    unsigned char header[9];
876    stbhw_config c = { 0 };
877    stbhw__process p = { 0 };
878 
879    // extract binary header
880 
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);
884 
885    // extract header info
886    if (header[7] == 0xc0) {
887       // corner-type
888       c.is_corner = 1;
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];
894    } else {
895       c.is_corner = 0;
896       // edge-type
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];
902    }
903 
904    if (c.num_vary_x < 0 || c.num_vary_x > 64 || c.num_vary_y < 0 || c.num_vary_y > 64)
905       return 0;
906    if (c.short_side_len == 0)
907       return 0;
908    if (c.num_color[0] > 32 || c.num_color[1] > 32 || c.num_color[2] > 32 || c.num_color[3] > 32)
909       return 0;
910 
911    stbhw__get_template_info(&c, NULL, NULL, &h_count, &v_count);
912 
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));
916 
917    ts->max_h_tiles = h_count;
918    ts->max_v_tiles = v_count;
919 
920    ts->num_h_tiles = ts->num_v_tiles = 0;
921 
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);
924 
925    p.ts = ts;
926    p.data = data;
927    p.stride = stride;
928    p.process_h_rect = stbhw__parse_h_rect;
929    p.process_v_rect = stbhw__parse_v_rect;
930    p.w = w;
931    p.h = h;
932    p.c = &c;
933 
934    // load all the tiles out of the image
935    return stbhw__process_template(&p);
936 }
937 
stbhw_free_tileset(stbhw_tileset * ts)938 STBHW_EXTERN void stbhw_free_tileset(stbhw_tileset *ts)
939 {
940    int i;
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]);
945    free(ts->h_tiles);
946    free(ts->v_tiles);
947    ts->h_tiles = NULL;
948    ts->v_tiles = NULL;
949    ts->num_h_tiles = ts->max_h_tiles = 0;
950    ts->num_v_tiles = ts->max_v_tiles = 0;
951 }
952 
953 //////////////////////////////////////////////////////////////////////////////
954 //
955 //               GENERATOR
956 //
957 //
958 
959 
960 // shared code
961 
stbhw__set_pixel(unsigned char * data,int stride,int xpos,int ypos,unsigned char color[3])962 static void stbhw__set_pixel(unsigned char *data, int stride, int xpos, int ypos, unsigned char color[3])
963 {
964    memcpy(data + ypos*stride + xpos*3, color, 3);
965 }
966 
stbhw__stbhw__set_pixel_whiten(unsigned char * data,int stride,int xpos,int ypos,unsigned char color[3])967 static void stbhw__stbhw__set_pixel_whiten(unsigned char *data, int stride, int xpos, int ypos, unsigned char color[3])
968 {
969    unsigned char c2[3];
970    int i;
971    for (i=0; i < 3; ++i)
972       c2[i] = (color[i]*2 + 255)/3;
973    memcpy(data + ypos*stride + xpos*3, c2, 3);
974 }
975 
976 
977 static unsigned char stbhw__black[3] = { 0,0,0 };
978 
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
982 
983 static unsigned char stbhw__color[7][8][3] =
984 {
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}, },
991 
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 } },
994 };
995 
stbhw__draw_hline(unsigned char * data,int stride,int xpos,int ypos,int color,int len,int slot)996 static void stbhw__draw_hline(unsigned char *data, int stride, int xpos, int ypos, int color, int len, int slot)
997 {
998    int i;
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);
1003    if (k-j < 2) {
1004       j = len/2 - 1;
1005       k = j+2;
1006       if (len & 1)
1007          ++k;
1008    }
1009    for (i=j; i < k; ++i)
1010       stbhw__stbhw__set_pixel_whiten(data, stride, xpos+i, ypos, stbhw__color[slot][color]);
1011 }
1012 
stbhw__draw_vline(unsigned char * data,int stride,int xpos,int ypos,int color,int len,int slot)1013 static void stbhw__draw_vline(unsigned char *data, int stride, int xpos, int ypos, int color, int len, int slot)
1014 {
1015    int i;
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);
1020    if (k-j < 2) {
1021       j = len/2 - 1;
1022       k = j+2;
1023       if (len & 1)
1024          ++k;
1025    }
1026    for (i=j; i < k; ++i)
1027       stbhw__stbhw__set_pixel_whiten(data, stride, xpos, ypos+i, stbhw__color[slot][color]);
1028 }
1029 
1030 //                 0--*--1--*--2--*--3
1031 //                 |     |           |
1032 //                 *     *           *
1033 //                 |     |           |
1034 //     1--*--2--*--3     0--*--1--*--2
1035 //     |           |     |
1036 //     *           *     *
1037 //     |           |     |
1038 //     0--*--1--*--2--*--3
1039 //
1040 // variables while enumerating (no correspondence between corners
1041 // of the types is implied by these variables)
1042 //
1043 //     a-----b-----c      a-----d
1044 //     |           |      |     |
1045 //     |           |      |     |
1046 //     |           |      |     |
1047 //     d-----e-----f      b     e
1048 //                        |     |
1049 //                        |     |
1050 //                        |     |
1051 //                        c-----f
1052 //
1053 
1054 unsigned char stbhw__corner_colors[4][4][3] =
1055 {
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 }, },
1060 };
1061 
1062 int stbhw__corner_colors_to_edge_color[4][4] =
1063 {
1064    // 0   1   2   3
1065    {  0,  1,  4,  9, }, // 0
1066    {  2,  3,  5, 10, }, // 1
1067    {  6,  7,  8, 11, }, // 2
1068    { 12, 13, 14, 15, }, // 3
1069 };
1070 
1071 #define stbhw__c2e stbhw__corner_colors_to_edge_color
1072 
stbhw__draw_clipped_corner(unsigned char * data,int stride,int xpos,int ypos,int w,int h,int x,int y)1073 static void stbhw__draw_clipped_corner(unsigned char *data, int stride, int xpos, int ypos, int w, int h, int x, int y)
1074 {
1075    static unsigned char template_color[3] = { 167,204,204 };
1076    int i,j;
1077    for (j = -2; j <= 1; ++j) {
1078       for (i = -2; i <= 1; ++i) {
1079          if ((i == -2 || i == 1) && (j == -2 || j == 1))
1080             continue;
1081          else {
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);
1085 
1086          }
1087       }
1088    }
1089 }
1090 
stbhw__edge_process_h_rect(stbhw__process * p,int xpos,int ypos,int a,int b,int c,int d,int e,int f)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)
1093 {
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);
1101 }
1102 
stbhw__edge_process_v_rect(stbhw__process * p,int xpos,int ypos,int a,int b,int c,int d,int e,int f)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)
1105 {
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);
1113 }
1114 
stbhw__corner_process_h_rect(stbhw__process * p,int xpos,int ypos,int a,int b,int c,int d,int e,int f)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)
1117 {
1118    int len = p->c->short_side_len;
1119 
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);
1126 
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);
1130 
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);
1134 
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]);
1141 }
1142 
stbhw__corner_process_v_rect(stbhw__process * p,int xpos,int ypos,int a,int b,int c,int d,int e,int 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)
1145 {
1146    int len = p->c->short_side_len;
1147 
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);
1154 
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);
1158 
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);
1162 
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]);
1169 }
1170 
1171 // generates a template image, assuming data is 3*w*h bytes long, RGB format
stbhw_make_template(stbhw_config * c,unsigned char * data,int w,int h,int stride_in_bytes)1172 STBHW_EXTERN int stbhw_make_template(stbhw_config *c, unsigned char *data, int w, int h, int stride_in_bytes)
1173 {
1174    stbhw__process p;
1175    int i;
1176 
1177    p.data = data;
1178    p.w = w;
1179    p.h = h;
1180    p.stride = stride_in_bytes;
1181    p.ts = 0;
1182    p.c = c;
1183 
1184    if (c->is_corner) {
1185       p.process_h_rect = stbhw__corner_process_h_rect;
1186       p.process_v_rect = stbhw__corner_process_v_rect;
1187    } else {
1188       p.process_h_rect = stbhw__edge_process_h_rect;
1189       p.process_v_rect = stbhw__edge_process_v_rect;
1190    }
1191 
1192    for (i=0; i < p.h; ++i)
1193       memset(p.data + i*p.stride, 255, 3*p.w);
1194 
1195    if (!stbhw__process_template(&p))
1196       return 0;
1197 
1198    if (c->is_corner) {
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;
1206    } else {
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;
1212    }
1213 
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;
1217 
1218    return 1;
1219 }
1220 #endif // STB_HBWANG_IMPLEMENTATION
1221