1 /* stbhw - v0.7 -  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.7   2019-03-04   - fix warnings
144 	0.6   2014-08-17   - fix broken map-maker
145 	0.5   2014-07-07   - initial release
146 
147 */
148 
149 //////////////////////////////////////////////////////////////////////////////
150 //                                                                          //
151 //                         HEADER FILE SECTION                              //
152 //                                                                          //
153 
154 #ifndef INCLUDE_STB_HWANG_H
155 #define INCLUDE_STB_HWANG_H
156 
157 #ifdef STB_HBWANG_STATIC
158 #define STBHW_EXTERN static
159 #else
160 #ifdef __cplusplus
161 #define STBHW_EXTERN extern "C"
162 #else
163 #define STBHW_EXTERN extern
164 #endif
165 #endif
166 
167 typedef struct stbhw_tileset stbhw_tileset;
168 
169 // returns description of last error produced by any function (not thread-safe)
170 STBHW_EXTERN const char *stbhw_get_last_error(void);
171 
172 // build a tileset from an image that conforms to a template created by this
173 // library. (you allocate storage for stbhw_tileset and function fills it out;
174 // memory for individual tiles are malloc()ed).
175 // returns non-zero on success, 0 on error
176 STBHW_EXTERN int stbhw_build_tileset_from_image(stbhw_tileset *ts,
177                      unsigned char *pixels, int stride_in_bytes, int w, int h);
178 
179 // free a tileset built by stbhw_build_tileset_from_image
180 STBHW_EXTERN void stbhw_free_tileset(stbhw_tileset *ts);
181 
182 // generate a map that is w * h pixels (3-bytes each)
183 // returns non-zero on success, 0 on error
184 // not thread-safe (uses a global data structure to avoid memory management)
185 // weighting should be NULL, as non-NULL weighting is currently untested
186 STBHW_EXTERN int stbhw_generate_image(stbhw_tileset *ts, int **weighting,
187                      unsigned char *pixels, int stride_in_bytes, int w, int h);
188 
189 //////////////////////////////////////
190 //
191 // TILESET DATA STRUCTURE
192 //
193 // if you use the image-to-tileset system from this file, you
194 // don't need to worry about these data structures. but if you
195 // want to build/load a tileset yourself, you'll need to fill
196 // these out.
197 
198 typedef struct
199 {
200    // the edge or vertex constraints, according to diagram below
201    signed char a,b,c,d,e,f;
202 
203    // The herringbone wang tile data; it is a bitmap which is either
204    // w=2*short_sidelen,h=short_sidelen, or w=short_sidelen,h=2*short_sidelen.
205    // it is always RGB, stored row-major, with no padding between rows.
206    // (allocate stbhw_tile structure to be large enough for the pixel data)
207    unsigned char pixels[1];
208 } stbhw_tile;
209 
210 struct stbhw_tileset
211 {
212    int is_corner;
213    int num_color[6];  // number of colors for each of 6 edge types or 4 corner types
214    int short_side_len;
215    stbhw_tile **h_tiles;
216    stbhw_tile **v_tiles;
217    int num_h_tiles, max_h_tiles;
218    int num_v_tiles, max_v_tiles;
219 };
220 
221 ///////////////  TEMPLATE GENERATOR  //////////////////////////
222 
223 // when requesting a template, you fill out this data
224 typedef struct
225 {
226    int is_corner;      // using corner colors or edge colors?
227    int short_side_len; // rectangles is 2n x n, n = short_side_len
228    int num_color[6];   // see below diagram for meaning of the index to this;
229                        // 6 values if edge (!is_corner), 4 values if is_corner
230                        // legal numbers: 1..8 if edge, 1..4 if is_corner
231    int num_vary_x;     // additional number of variations along x axis in the template
232    int num_vary_y;     // additional number of variations along y axis in the template
233    int corner_type_color_template[4][4];
234       // if corner_type_color_template[s][t] is non-zero, then any
235       // corner of type s generated as color t will get a little
236       // corner sample markup in the template image data
237 
238 } stbhw_config;
239 
240 // computes the size needed for the template image
241 STBHW_EXTERN void stbhw_get_template_size(stbhw_config *c, int *w, int *h);
242 
243 // generates a template image, assuming data is 3*w*h bytes long, RGB format
244 STBHW_EXTERN int stbhw_make_template(stbhw_config *c, unsigned char *data, int w, int h, int stride_in_bytes);
245 
246 #endif//INCLUDE_STB_HWANG_H
247 
248 
249 // TILE CONSTRAINT TYPES
250 //
251 // there are 4 "types" of corners and 6 types of edges.
252 // you can configure the tileset to have different numbers
253 // of colors for each type of color or edge.
254 //
255 // corner types:
256 //
257 //                     0---*---1---*---2---*---3
258 //                     |       |               |
259 //                     *       *               *
260 //                     |       |               |
261 //     1---*---2---*---3       0---*---1---*---2
262 //     |               |       |
263 //     *               *       *
264 //     |               |       |
265 //     0---*---1---*---2---*---3
266 //
267 //
268 //  edge types:
269 //
270 //     *---2---*---3---*      *---0---*
271 //     |               |      |       |
272 //     1               4      5       1
273 //     |               |      |       |
274 //     *---0---*---2---*      *       *
275 //                            |       |
276 //                            4       5
277 //                            |       |
278 //                            *---3---*
279 //
280 // TILE CONSTRAINTS
281 //
282 // each corner/edge has a color; this shows the name
283 // of the variable containing the color
284 //
285 // corner constraints:
286 //
287 //                        a---*---d
288 //                        |       |
289 //                        *       *
290 //                        |       |
291 //     a---*---b---*---c  b       e
292 //     |               |  |       |
293 //     *               *  *       *
294 //     |               |  |       |
295 //     d---*---e---*---f  c---*---f
296 //
297 //
298 //  edge constraints:
299 //
300 //     *---a---*---b---*      *---a---*
301 //     |               |      |       |
302 //     c               d      b       c
303 //     |               |      |       |
304 //     *---e---*---f---*      *       *
305 //                            |       |
306 //                            d       e
307 //                            |       |
308 //                            *---f---*
309 //
310 
311 
312 //////////////////////////////////////////////////////////////////////////////
313 //                                                                          //
314 //                       IMPLEMENTATION SECTION                             //
315 //                                                                          //
316 
317 #ifdef STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION
318 
319 
320 #include <string.h> // memcpy
321 #include <stdlib.h> // malloc
322 
323 #ifndef STB_HBWANG_RAND
324 #include <stdlib.h>
325 #define STB_HBWANG_RAND()  (rand() >> 4)
326 #endif
327 
328 #ifndef STB_HBWANG_ASSERT
329 #include <assert.h>
330 #define STB_HBWANG_ASSERT(x)  assert(x)
331 #endif
332 
333 // map size
334 #ifndef STB_HBWANG_MAX_X
335 #define STB_HBWANG_MAX_X  100
336 #endif
337 
338 #ifndef STB_HBWANG_MAX_Y
339 #define STB_HBWANG_MAX_Y  100
340 #endif
341 
342 // global variables for color assignments
343 // @MEMORY change these to just store last two/three rows
344 //         and keep them on the stack
345 static signed char c_color[STB_HBWANG_MAX_Y+6][STB_HBWANG_MAX_X+6];
346 static signed char v_color[STB_HBWANG_MAX_Y+6][STB_HBWANG_MAX_X+5];
347 static signed char h_color[STB_HBWANG_MAX_Y+5][STB_HBWANG_MAX_X+6];
348 
349 static const char *stbhw_error;
stbhw_get_last_error(void)350 STBHW_EXTERN const char *stbhw_get_last_error(void)
351 {
352    const char *temp = stbhw_error;
353    stbhw_error = 0;
354    return temp;
355 }
356 
357 
358 
359 
360 /////////////////////////////////////////////////////////////
361 //
362 //  SHARED TEMPLATE-DESCRIPTION CODE
363 //
364 //  Used by both template generator and tileset parser; by
365 //  using the same code, they are locked in sync and we don't
366 //  need to try to do more sophisticated parsing of edge color
367 //  markup or something.
368 
369 typedef void stbhw__process_rect(struct stbhw__process *p, int xpos, int ypos,
370                                  int a, int b, int c, int d, int e, int f);
371 
372 typedef struct stbhw__process
373 {
374    stbhw_tileset *ts;
375    stbhw_config *c;
376    stbhw__process_rect *process_h_rect;
377    stbhw__process_rect *process_v_rect;
378    unsigned char *data;
379    int stride,w,h;
380 } stbhw__process;
381 
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)382 static void stbhw__process_h_row(stbhw__process *p,
383                            int xpos, int ypos,
384                            int a0, int a1,
385                            int b0, int b1,
386                            int c0, int c1,
387                            int d0, int d1,
388                            int e0, int e1,
389                            int f0, int f1,
390                            int variants)
391 {
392    int a,b,c,d,e,f,v;
393 
394    for (v=0; v < variants; ++v)
395       for (f=f0; f <= f1; ++f)
396          for (e=e0; e <= e1; ++e)
397             for (d=d0; d <= d1; ++d)
398                for (c=c0; c <= c1; ++c)
399                   for (b=b0; b <= b1; ++b)
400                      for (a=a0; a <= a1; ++a) {
401                         p->process_h_rect(p, xpos, ypos, a,b,c,d,e,f);
402                         xpos += 2*p->c->short_side_len + 3;
403                      }
404 }
405 
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)406 static void stbhw__process_v_row(stbhw__process *p,
407                            int xpos, int ypos,
408                            int a0, int a1,
409                            int b0, int b1,
410                            int c0, int c1,
411                            int d0, int d1,
412                            int e0, int e1,
413                            int f0, int f1,
414                            int variants)
415 {
416    int a,b,c,d,e,f,v;
417 
418    for (v=0; v < variants; ++v)
419       for (f=f0; f <= f1; ++f)
420          for (e=e0; e <= e1; ++e)
421             for (d=d0; d <= d1; ++d)
422                for (c=c0; c <= c1; ++c)
423                   for (b=b0; b <= b1; ++b)
424                      for (a=a0; a <= a1; ++a) {
425                         p->process_v_rect(p, xpos, ypos, a,b,c,d,e,f);
426                         xpos += p->c->short_side_len+3;
427                      }
428 }
429 
stbhw__get_template_info(stbhw_config * c,int * w,int * h,int * h_count,int * v_count)430 static void stbhw__get_template_info(stbhw_config *c, int *w, int *h, int *h_count, int *v_count)
431 {
432    int size_x,size_y;
433    int horz_count,vert_count;
434 
435    if (c->is_corner) {
436       int horz_w = c->num_color[1] * c->num_color[2] * c->num_color[3] * c->num_vary_x;
437       int horz_h = c->num_color[0] * c->num_color[1] * c->num_color[2] * c->num_vary_y;
438 
439       int vert_w = c->num_color[0] * c->num_color[3] * c->num_color[2] * c->num_vary_y;
440       int vert_h = c->num_color[1] * c->num_color[0] * c->num_color[3] * c->num_vary_x;
441 
442       int horz_x = horz_w * (2*c->short_side_len + 3);
443       int horz_y = horz_h * (  c->short_side_len + 3);
444 
445       int vert_x = vert_w * (  c->short_side_len + 3);
446       int vert_y = vert_h * (2*c->short_side_len + 3);
447 
448       horz_count = horz_w * horz_h;
449       vert_count = vert_w * vert_h;
450 
451       size_x = horz_x > vert_x ? horz_x : vert_x;
452       size_y = 2 + horz_y + 2 + vert_y;
453    } else {
454       int horz_w = c->num_color[0] * c->num_color[1] * c->num_color[2] * c->num_vary_x;
455       int horz_h = c->num_color[3] * c->num_color[4] * c->num_color[2] * c->num_vary_y;
456 
457       int vert_w = c->num_color[0] * c->num_color[5] * c->num_color[1] * c->num_vary_y;
458       int vert_h = c->num_color[3] * c->num_color[4] * c->num_color[5] * c->num_vary_x;
459 
460       int horz_x = horz_w * (2*c->short_side_len + 3);
461       int horz_y = horz_h * (  c->short_side_len + 3);
462 
463       int vert_x = vert_w * (  c->short_side_len + 3);
464       int vert_y = vert_h * (2*c->short_side_len + 3);
465 
466       horz_count = horz_w * horz_h;
467       vert_count = vert_w * vert_h;
468 
469       size_x = horz_x > vert_x ? horz_x : vert_x;
470       size_y = 2 + horz_y + 2 + vert_y;
471    }
472    if (w) *w = size_x;
473    if (h) *h = size_y;
474    if (h_count) *h_count = horz_count;
475    if (v_count) *v_count = vert_count;
476 }
477 
stbhw_get_template_size(stbhw_config * c,int * w,int * h)478 STBHW_EXTERN void stbhw_get_template_size(stbhw_config *c, int *w, int *h)
479 {
480    stbhw__get_template_info(c, w, h, NULL, NULL);
481 }
482 
stbhw__process_template(stbhw__process * p)483 static int stbhw__process_template(stbhw__process *p)
484 {
485    int i,j,k,q, ypos;
486    int size_x, size_y;
487    stbhw_config *c = p->c;
488 
489    stbhw__get_template_info(c, &size_x, &size_y, NULL, NULL);
490 
491    if (p->w < size_x || p->h < size_y) {
492       stbhw_error = "image too small for configuration";
493       return 0;
494    }
495 
496    if (c->is_corner) {
497       ypos = 2;
498       for (k=0; k < c->num_color[2]; ++k) {
499          for (j=0; j < c->num_color[1]; ++j) {
500             for (i=0; i < c->num_color[0]; ++i) {
501                for (q=0; q < c->num_vary_y; ++q) {
502                   stbhw__process_h_row(p, 0,ypos,
503                      0,c->num_color[1]-1, 0,c->num_color[2]-1, 0,c->num_color[3]-1,
504                      i,i, j,j, k,k,
505                      c->num_vary_x);
506                   ypos += c->short_side_len + 3;
507                }
508             }
509          }
510       }
511       ypos += 2;
512       for (k=0; k < c->num_color[3]; ++k) {
513          for (j=0; j < c->num_color[0]; ++j) {
514             for (i=0; i < c->num_color[1]; ++i) {
515                for (q=0; q < c->num_vary_x; ++q) {
516                   stbhw__process_v_row(p, 0,ypos,
517                      0,c->num_color[0]-1, 0,c->num_color[3]-1, 0,c->num_color[2]-1,
518                      i,i, j,j, k,k,
519                      c->num_vary_y);
520                   ypos += (c->short_side_len*2) + 3;
521                }
522             }
523          }
524       }
525       assert(ypos == size_y);
526    } else {
527       ypos = 2;
528       for (k=0; k < c->num_color[3]; ++k) {
529          for (j=0; j < c->num_color[4]; ++j) {
530             for (i=0; i < c->num_color[2]; ++i) {
531                for (q=0; q < c->num_vary_y; ++q) {
532                   stbhw__process_h_row(p, 0,ypos,
533                      0,c->num_color[2]-1, k,k,
534                      0,c->num_color[1]-1, j,j,
535                      0,c->num_color[0]-1, i,i,
536                      c->num_vary_x);
537                   ypos += c->short_side_len + 3;
538                }
539             }
540          }
541       }
542       ypos += 2;
543       for (k=0; k < c->num_color[3]; ++k) {
544          for (j=0; j < c->num_color[4]; ++j) {
545             for (i=0; i < c->num_color[5]; ++i) {
546                for (q=0; q < c->num_vary_x; ++q) {
547                   stbhw__process_v_row(p, 0,ypos,
548                      0,c->num_color[0]-1, i,i,
549                      0,c->num_color[1]-1, j,j,
550                      0,c->num_color[5]-1, k,k,
551                      c->num_vary_y);
552                   ypos += (c->short_side_len*2) + 3;
553                }
554             }
555          }
556       }
557       assert(ypos == size_y);
558    }
559    return 1;
560 }
561 
562 
563 /////////////////////////////////////////////////////////////
564 //
565 //  MAP GENERATOR
566 //
567 
stbhw__draw_pixel(unsigned char * output,int stride,int x,int y,unsigned char c[3])568 static void stbhw__draw_pixel(unsigned char *output, int stride, int x, int y, unsigned char c[3])
569 {
570    memcpy(output + y*stride + x*3, c, 3);
571 }
572 
stbhw__draw_h_tile(unsigned char * output,int stride,int xmax,int ymax,int x,int y,stbhw_tile * h,int sz)573 static void stbhw__draw_h_tile(unsigned char *output, int stride, int xmax, int ymax, int x, int y, stbhw_tile *h, int sz)
574 {
575    int i,j;
576    for (j=0; j < sz; ++j)
577       if (y+j >= 0 && y+j < ymax)
578          for (i=0; i < sz*2; ++i)
579             if (x+i >= 0 && x+i < xmax)
580                stbhw__draw_pixel(output,stride, x+i,y+j, &h->pixels[(j*sz*2 + i)*3]);
581 }
582 
stbhw__draw_v_tile(unsigned char * output,int stride,int xmax,int ymax,int x,int y,stbhw_tile * h,int sz)583 static void stbhw__draw_v_tile(unsigned char *output, int stride, int xmax, int ymax, int x, int y, stbhw_tile *h, int sz)
584 {
585    int i,j;
586    for (j=0; j < sz*2; ++j)
587       if (y+j >= 0 && y+j < ymax)
588          for (i=0; i < sz; ++i)
589             if (x+i >= 0 && x+i < xmax)
590                stbhw__draw_pixel(output,stride, x+i,y+j, &h->pixels[(j*sz + i)*3]);
591 }
592 
593 
594 // 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)595 static stbhw_tile * stbhw__choose_tile(stbhw_tile **list, int numlist,
596                                       signed char *a, signed char *b, signed char *c,
597                                       signed char *d, signed char *e, signed char *f,
598                                       int **weighting)
599 {
600    int i,n,m = 1<<30,pass;
601    for (pass=0; pass < 2; ++pass) {
602       n=0;
603       // pass #1:
604       //   count number of variants that match this partial set of constraints
605       // pass #2:
606       //   stop on randomly selected match
607       for (i=0; i < numlist; ++i) {
608          stbhw_tile *h = list[i];
609          if ((*a < 0 || *a == h->a) &&
610              (*b < 0 || *b == h->b) &&
611              (*c < 0 || *c == h->c) &&
612              (*d < 0 || *d == h->d) &&
613              (*e < 0 || *e == h->e) &&
614              (*f < 0 || *f == h->f)) {
615             if (weighting)
616                n += weighting[0][i];
617             else
618                n += 1;
619             if (n > m) {
620                // use list[i]
621                // update constraints to reflect what we placed
622                *a = h->a;
623                *b = h->b;
624                *c = h->c;
625                *d = h->d;
626                *e = h->e;
627                *f = h->f;
628                return h;
629             }
630          }
631       }
632       if (n == 0) {
633          stbhw_error = "couldn't find tile matching constraints";
634          return NULL;
635       }
636       m = STB_HBWANG_RAND() % n;
637    }
638    STB_HBWANG_ASSERT(0);
639    return NULL;
640 }
641 
stbhw__match(int x,int y)642 static int stbhw__match(int x, int y)
643 {
644    return c_color[y][x] == c_color[y+1][x+1];
645 }
646 
stbhw__weighted(int num_options,int * weights)647 static int stbhw__weighted(int num_options, int *weights)
648 {
649    int k, total, choice;
650    total = 0;
651    for (k=0; k < num_options; ++k)
652       total += weights[k];
653    choice = STB_HBWANG_RAND() % total;
654    total = 0;
655    for (k=0; k < num_options; ++k) {
656       total += weights[k];
657       if (choice < total)
658          break;
659    }
660    STB_HBWANG_ASSERT(k < num_options);
661    return k;
662 }
663 
stbhw__change_color(int old_color,int num_options,int * weights)664 static int stbhw__change_color(int old_color, int num_options, int *weights)
665 {
666    if (weights) {
667       int k, total, choice;
668       total = 0;
669       for (k=0; k < num_options; ++k)
670          if (k != old_color)
671             total += weights[k];
672       choice = STB_HBWANG_RAND() % total;
673       total = 0;
674       for (k=0; k < num_options; ++k) {
675          if (k != old_color) {
676             total += weights[k];
677             if (choice < total)
678                break;
679          }
680       }
681       STB_HBWANG_ASSERT(k < num_options);
682       return k;
683    } else {
684       int offset = 1+STB_HBWANG_RAND() % (num_options-1);
685       return (old_color+offset) % num_options;
686    }
687 }
688 
689 
690 
691 // generate a map that is w * h pixels (3-bytes each)
692 // returns 1 on success, 0 on error
stbhw_generate_image(stbhw_tileset * ts,int ** weighting,unsigned char * output,int stride,int w,int h)693 STBHW_EXTERN int stbhw_generate_image(stbhw_tileset *ts, int **weighting, unsigned char *output, int stride, int w, int h)
694 {
695    int sidelen = ts->short_side_len;
696    int xmax = (w / sidelen) + 6;
697    int ymax = (h / sidelen) + 6;
698    if (xmax > STB_HBWANG_MAX_X+6 || ymax > STB_HBWANG_MAX_Y+6) {
699       stbhw_error = "increase STB_HBWANG_MAX_X/Y";
700       return 0;
701    }
702 
703    if (ts->is_corner) {
704       int i,j, ypos;
705       int *cc = ts->num_color;
706 
707       for (j=0; j < ymax; ++j) {
708          for (i=0; i < xmax; ++i) {
709             int p = (i-j+1)&3; // corner type
710             if (weighting==NULL || weighting[p]==0 || cc[p] == 1)
711                c_color[j][i] = STB_HBWANG_RAND() % cc[p];
712             else
713                c_color[j][i] = stbhw__weighted(cc[p], weighting[p]);
714          }
715       }
716       #ifndef STB_HBWANG_NO_REPITITION_REDUCTION
717       // now go back through and make sure we don't have adjancent 3x2 vertices that are identical,
718       // to avoid really obvious repetition (which happens easily with extreme weights)
719       for (j=0; j < ymax-3; ++j) {
720          for (i=0; i < xmax-3; ++i) {
721             //int p = (i-j+1) & 3; // corner type   // unused, not sure what the intent was so commenting it out
722             STB_HBWANG_ASSERT(i+3 < STB_HBWANG_MAX_X+6);
723             STB_HBWANG_ASSERT(j+3 < STB_HBWANG_MAX_Y+6);
724             if (stbhw__match(i,j) && stbhw__match(i,j+1) && stbhw__match(i,j+2)
725                 && stbhw__match(i+1,j) && stbhw__match(i+1,j+1) && stbhw__match(i+1,j+2)) {
726                int p = ((i+1)-(j+1)+1) & 3;
727                if (cc[p] > 1)
728                   c_color[j+1][i+1] = stbhw__change_color(c_color[j+1][i+1], cc[p], weighting ? weighting[p] : NULL);
729             }
730             if (stbhw__match(i,j) && stbhw__match(i+1,j) && stbhw__match(i+2,j)
731                 && stbhw__match(i,j+1) && stbhw__match(i+1,j+1) && stbhw__match(i+2,j+1)) {
732                int p = ((i+2)-(j+1)+1) & 3;
733                if (cc[p] > 1)
734                   c_color[j+1][i+2] = stbhw__change_color(c_color[j+1][i+2], cc[p], weighting ? weighting[p] : NULL);
735             }
736          }
737       }
738       #endif
739 
740       ypos = -1 * sidelen;
741       for (j = -1; ypos < h; ++j) {
742          // a general herringbone row consists of:
743          //    horizontal left block, the bottom of a previous vertical, the top of a new vertical
744          int phase = (j & 3);
745          // displace horizontally according to pattern
746          if (phase == 0) {
747             i = 0;
748          } else {
749             i = phase-4;
750          }
751          for (;; i += 4) {
752             int xpos = i * sidelen;
753             if (xpos >= w)
754                break;
755             // horizontal left-block
756             if (xpos + sidelen*2 >= 0 && ypos >= 0) {
757                stbhw_tile *t = stbhw__choose_tile(
758                   ts->h_tiles, ts->num_h_tiles,
759                   &c_color[j+2][i+2], &c_color[j+2][i+3], &c_color[j+2][i+4],
760                   &c_color[j+3][i+2], &c_color[j+3][i+3], &c_color[j+3][i+4],
761                   weighting
762                );
763                if (t == NULL)
764                   return 0;
765                stbhw__draw_h_tile(output,stride,w,h, xpos, ypos, t, sidelen);
766             }
767             xpos += sidelen * 2;
768             // now we're at the end of a previous vertical one
769             xpos += sidelen;
770             // now we're at the start of a new vertical one
771             if (xpos < w) {
772                stbhw_tile *t = stbhw__choose_tile(
773                   ts->v_tiles, ts->num_v_tiles,
774                   &c_color[j+2][i+5], &c_color[j+3][i+5], &c_color[j+4][i+5],
775                   &c_color[j+2][i+6], &c_color[j+3][i+6], &c_color[j+4][i+6],
776                   weighting
777                );
778                if (t == NULL)
779                   return 0;
780                stbhw__draw_v_tile(output,stride,w,h, xpos, ypos,  t, sidelen);
781             }
782          }
783          ypos += sidelen;
784       }
785    } else {
786       // @TODO edge-color repetition reduction
787       int i,j, ypos;
788       memset(v_color, -1, sizeof(v_color));
789       memset(h_color, -1, sizeof(h_color));
790 
791       ypos = -1 * sidelen;
792       for (j = -1; ypos<h; ++j) {
793          // a general herringbone row consists of:
794          //    horizontal left block, the bottom of a previous vertical, the top of a new vertical
795          int phase = (j & 3);
796          // displace horizontally according to pattern
797          if (phase == 0) {
798             i = 0;
799          } else {
800             i = phase-4;
801          }
802          for (;; i += 4) {
803             int xpos = i * sidelen;
804             if (xpos >= w)
805                break;
806             // horizontal left-block
807             if (xpos + sidelen*2 >= 0 && ypos >= 0) {
808                stbhw_tile *t = stbhw__choose_tile(
809                   ts->h_tiles, ts->num_h_tiles,
810                   &h_color[j+2][i+2], &h_color[j+2][i+3],
811                   &v_color[j+2][i+2], &v_color[j+2][i+4],
812                   &h_color[j+3][i+2], &h_color[j+3][i+3],
813                   weighting
814                );
815                if (t == NULL) return 0;
816                stbhw__draw_h_tile(output,stride,w,h, xpos, ypos, t, sidelen);
817             }
818             xpos += sidelen * 2;
819             // now we're at the end of a previous vertical one
820             xpos += sidelen;
821             // now we're at the start of a new vertical one
822             if (xpos < w) {
823                stbhw_tile *t = stbhw__choose_tile(
824                   ts->v_tiles, ts->num_v_tiles,
825                   &h_color[j+2][i+5],
826                   &v_color[j+2][i+5], &v_color[j+2][i+6],
827                   &v_color[j+3][i+5], &v_color[j+3][i+6],
828                   &h_color[j+4][i+5],
829                   weighting
830                );
831                if (t == NULL) return 0;
832                stbhw__draw_v_tile(output,stride,w,h, xpos, ypos,  t, sidelen);
833             }
834          }
835          ypos += sidelen;
836       }
837    }
838    return 1;
839 }
840 
stbhw__parse_h_rect(stbhw__process * p,int xpos,int ypos,int a,int b,int c,int d,int e,int f)841 static void stbhw__parse_h_rect(stbhw__process *p, int xpos, int ypos,
842                             int a, int b, int c, int d, int e, int f)
843 {
844    int len = p->c->short_side_len;
845    stbhw_tile *h = (stbhw_tile *) malloc(sizeof(*h)-1 + 3 * (len*2) * len);
846    int i,j;
847    ++xpos;
848    ++ypos;
849    h->a = a, h->b = b, h->c = c, h->d = d, h->e = e, h->f = f;
850    for (j=0; j < len; ++j)
851       for (i=0; i < len*2; ++i)
852          memcpy(h->pixels + j*(3*len*2) + i*3, p->data+(ypos+j)*p->stride+(xpos+i)*3, 3);
853    STB_HBWANG_ASSERT(p->ts->num_h_tiles < p->ts->max_h_tiles);
854    p->ts->h_tiles[p->ts->num_h_tiles++] = h;
855 }
856 
stbhw__parse_v_rect(stbhw__process * p,int xpos,int ypos,int a,int b,int c,int d,int e,int f)857 static void stbhw__parse_v_rect(stbhw__process *p, int xpos, int ypos,
858                             int a, int b, int c, int d, int e, int f)
859 {
860    int len = p->c->short_side_len;
861    stbhw_tile *h = (stbhw_tile *) malloc(sizeof(*h)-1 + 3 * (len*2) * len);
862    int i,j;
863    ++xpos;
864    ++ypos;
865    h->a = a, h->b = b, h->c = c, h->d = d, h->e = e, h->f = f;
866    for (j=0; j < len*2; ++j)
867       for (i=0; i < len; ++i)
868          memcpy(h->pixels + j*(3*len) + i*3, p->data+(ypos+j)*p->stride+(xpos+i)*3, 3);
869    STB_HBWANG_ASSERT(p->ts->num_v_tiles < p->ts->max_v_tiles);
870    p->ts->v_tiles[p->ts->num_v_tiles++] = h;
871 }
872 
stbhw_build_tileset_from_image(stbhw_tileset * ts,unsigned char * data,int stride,int w,int h)873 STBHW_EXTERN int stbhw_build_tileset_from_image(stbhw_tileset *ts, unsigned char *data, int stride, int w, int h)
874 {
875    int i, h_count, v_count;
876    unsigned char header[9];
877    stbhw_config c = { 0 };
878    stbhw__process p = { 0 };
879 
880    // extract binary header
881 
882    // remove encoding that makes it more visually obvious it encodes actual data
883    for (i=0; i < 9; ++i)
884       header[i] = data[w*3 - 1 - i] ^ (i*55);
885 
886    // extract header info
887    if (header[7] == 0xc0) {
888       // corner-type
889       c.is_corner = 1;
890       for (i=0; i < 4; ++i)
891          c.num_color[i] = header[i];
892       c.num_vary_x = header[4];
893       c.num_vary_y = header[5];
894       c.short_side_len = header[6];
895    } else {
896       c.is_corner = 0;
897       // edge-type
898       for (i=0; i < 6; ++i)
899          c.num_color[i] = header[i];
900       c.num_vary_x = header[6];
901       c.num_vary_y = header[7];
902       c.short_side_len = header[8];
903    }
904 
905    if (c.num_vary_x < 0 || c.num_vary_x > 64 || c.num_vary_y < 0 || c.num_vary_y > 64)
906       return 0;
907    if (c.short_side_len == 0)
908       return 0;
909    if (c.num_color[0] > 32 || c.num_color[1] > 32 || c.num_color[2] > 32 || c.num_color[3] > 32)
910       return 0;
911 
912    stbhw__get_template_info(&c, NULL, NULL, &h_count, &v_count);
913 
914    ts->is_corner = c.is_corner;
915    ts->short_side_len = c.short_side_len;
916    memcpy(ts->num_color, c.num_color, sizeof(ts->num_color));
917 
918    ts->max_h_tiles = h_count;
919    ts->max_v_tiles = v_count;
920 
921    ts->num_h_tiles = ts->num_v_tiles = 0;
922 
923    ts->h_tiles = (stbhw_tile **) malloc(sizeof(*ts->h_tiles) * h_count);
924    ts->v_tiles = (stbhw_tile **) malloc(sizeof(*ts->v_tiles) * v_count);
925 
926    p.ts = ts;
927    p.data = data;
928    p.stride = stride;
929    p.process_h_rect = stbhw__parse_h_rect;
930    p.process_v_rect = stbhw__parse_v_rect;
931    p.w = w;
932    p.h = h;
933    p.c = &c;
934 
935    // load all the tiles out of the image
936    return stbhw__process_template(&p);
937 }
938 
stbhw_free_tileset(stbhw_tileset * ts)939 STBHW_EXTERN void stbhw_free_tileset(stbhw_tileset *ts)
940 {
941    int i;
942    for (i=0; i < ts->num_h_tiles; ++i)
943       free(ts->h_tiles[i]);
944    for (i=0; i < ts->num_v_tiles; ++i)
945       free(ts->v_tiles[i]);
946    free(ts->h_tiles);
947    free(ts->v_tiles);
948    ts->h_tiles = NULL;
949    ts->v_tiles = NULL;
950    ts->num_h_tiles = ts->max_h_tiles = 0;
951    ts->num_v_tiles = ts->max_v_tiles = 0;
952 }
953 
954 //////////////////////////////////////////////////////////////////////////////
955 //
956 //               GENERATOR
957 //
958 //
959 
960 
961 // shared code
962 
stbhw__set_pixel(unsigned char * data,int stride,int xpos,int ypos,unsigned char color[3])963 static void stbhw__set_pixel(unsigned char *data, int stride, int xpos, int ypos, unsigned char color[3])
964 {
965    memcpy(data + ypos*stride + xpos*3, color, 3);
966 }
967 
stbhw__stbhw__set_pixel_whiten(unsigned char * data,int stride,int xpos,int ypos,unsigned char color[3])968 static void stbhw__stbhw__set_pixel_whiten(unsigned char *data, int stride, int xpos, int ypos, unsigned char color[3])
969 {
970    unsigned char c2[3];
971    int i;
972    for (i=0; i < 3; ++i)
973       c2[i] = (color[i]*2 + 255)/3;
974    memcpy(data + ypos*stride + xpos*3, c2, 3);
975 }
976 
977 
978 static unsigned char stbhw__black[3] = { 0,0,0 };
979 
980 // each edge set gets its own unique color variants
981 // used http://phrogz.net/css/distinct-colors.html to generate this set,
982 // but it's not very good and needs to be revised
983 
984 static unsigned char stbhw__color[7][8][3] =
985 {
986    { {255,51,51}  , {143,143,29}, {0,199,199}, {159,119,199},     {0,149,199}  , {143, 0,143}, {255,128,0}, {64,255,0},  },
987    { {235,255,30 }, {255,0,255},  {199,139,119},  {29,143, 57},    {143,0,71}   , { 0,143,143}, {0,99,199}, {143,71,0},  },
988    { {0,149,199}  , {143, 0,143}, {255,128,0}, {64,255,0},        {255,191,0}  , {51,255,153}, {0,0,143}, {199,119,159},},
989    { {143,0,71}   , { 0,143,143}, {0,99,199}, {143,71,0},         {255,190,153}, { 0,255,255}, {128,0,255}, {255,51,102},},
990    { {255,191,0}  , {51,255,153}, {0,0,143}, {199,119,159},       {255,51,51}  , {143,143,29}, {0,199,199}, {159,119,199},},
991    { {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 
993    { {40,40,40 },  { 90,90,90 }, { 150,150,150 }, { 200,200,200 },
994      { 255,90,90 }, { 160,160,80}, { 50,150,150 }, { 200,50,200 } },
995 };
996 
stbhw__draw_hline(unsigned char * data,int stride,int xpos,int ypos,int color,int len,int slot)997 static void stbhw__draw_hline(unsigned char *data, int stride, int xpos, int ypos, int color, int len, int slot)
998 {
999    int i;
1000    int j = len * 6 / 16;
1001    int k = len * 10 / 16;
1002    for (i=0; i < len; ++i)
1003       stbhw__set_pixel(data, stride, xpos+i, ypos, stbhw__black);
1004    if (k-j < 2) {
1005       j = len/2 - 1;
1006       k = j+2;
1007       if (len & 1)
1008          ++k;
1009    }
1010    for (i=j; i < k; ++i)
1011       stbhw__stbhw__set_pixel_whiten(data, stride, xpos+i, ypos, stbhw__color[slot][color]);
1012 }
1013 
stbhw__draw_vline(unsigned char * data,int stride,int xpos,int ypos,int color,int len,int slot)1014 static void stbhw__draw_vline(unsigned char *data, int stride, int xpos, int ypos, int color, int len, int slot)
1015 {
1016    int i;
1017    int j = len * 6 / 16;
1018    int k = len * 10 / 16;
1019    for (i=0; i < len; ++i)
1020       stbhw__set_pixel(data, stride, xpos, ypos+i, stbhw__black);
1021    if (k-j < 2) {
1022       j = len/2 - 1;
1023       k = j+2;
1024       if (len & 1)
1025          ++k;
1026    }
1027    for (i=j; i < k; ++i)
1028       stbhw__stbhw__set_pixel_whiten(data, stride, xpos, ypos+i, stbhw__color[slot][color]);
1029 }
1030 
1031 //                 0--*--1--*--2--*--3
1032 //                 |     |           |
1033 //                 *     *           *
1034 //                 |     |           |
1035 //     1--*--2--*--3     0--*--1--*--2
1036 //     |           |     |
1037 //     *           *     *
1038 //     |           |     |
1039 //     0--*--1--*--2--*--3
1040 //
1041 // variables while enumerating (no correspondence between corners
1042 // of the types is implied by these variables)
1043 //
1044 //     a-----b-----c      a-----d
1045 //     |           |      |     |
1046 //     |           |      |     |
1047 //     |           |      |     |
1048 //     d-----e-----f      b     e
1049 //                        |     |
1050 //                        |     |
1051 //                        |     |
1052 //                        c-----f
1053 //
1054 
1055 unsigned char stbhw__corner_colors[4][4][3] =
1056 {
1057    { { 255,0,0 }, { 200,200,200 }, { 100,100,200 }, { 255,200,150 }, },
1058    { { 0,0,255 }, { 255,255,0 },   { 100,200,100 }, { 150,255,200 }, },
1059    { { 255,0,255 }, { 80,80,80 },  { 200,100,100 }, { 200,150,255 }, },
1060    { { 0,255,255 }, { 0,255,0 },   { 200,120,200 }, { 255,200,200 }, },
1061 };
1062 
1063 int stbhw__corner_colors_to_edge_color[4][4] =
1064 {
1065    // 0   1   2   3
1066    {  0,  1,  4,  9, }, // 0
1067    {  2,  3,  5, 10, }, // 1
1068    {  6,  7,  8, 11, }, // 2
1069    { 12, 13, 14, 15, }, // 3
1070 };
1071 
1072 #define stbhw__c2e stbhw__corner_colors_to_edge_color
1073 
stbhw__draw_clipped_corner(unsigned char * data,int stride,int xpos,int ypos,int w,int h,int x,int y)1074 static void stbhw__draw_clipped_corner(unsigned char *data, int stride, int xpos, int ypos, int w, int h, int x, int y)
1075 {
1076    static unsigned char template_color[3] = { 167,204,204 };
1077    int i,j;
1078    for (j = -2; j <= 1; ++j) {
1079       for (i = -2; i <= 1; ++i) {
1080          if ((i == -2 || i == 1) && (j == -2 || j == 1))
1081             continue;
1082          else {
1083             if (x+i < 1 || x+i > w) continue;
1084             if (y+j < 1 || y+j > h) continue;
1085             stbhw__set_pixel(data, stride, xpos+x+i, ypos+y+j, template_color);
1086 
1087          }
1088       }
1089    }
1090 }
1091 
stbhw__edge_process_h_rect(stbhw__process * p,int xpos,int ypos,int a,int b,int c,int d,int e,int f)1092 static void stbhw__edge_process_h_rect(stbhw__process *p, int xpos, int ypos,
1093                             int a, int b, int c, int d, int e, int f)
1094 {
1095    int len = p->c->short_side_len;
1096    stbhw__draw_hline(p->data, p->stride, xpos+1        , ypos        , a, len, 2);
1097    stbhw__draw_hline(p->data, p->stride, xpos+  len+1  , ypos        , b, len, 3);
1098    stbhw__draw_vline(p->data, p->stride, xpos          , ypos+1      , c, len, 1);
1099    stbhw__draw_vline(p->data, p->stride, xpos+2*len+1  , ypos+1      , d, len, 4);
1100    stbhw__draw_hline(p->data, p->stride, xpos+1        , ypos + len+1, e, len, 0);
1101    stbhw__draw_hline(p->data, p->stride, xpos + len+1  , ypos + len+1, f, len, 2);
1102 }
1103 
stbhw__edge_process_v_rect(stbhw__process * p,int xpos,int ypos,int a,int b,int c,int d,int e,int f)1104 static void stbhw__edge_process_v_rect(stbhw__process *p, int xpos, int ypos,
1105                             int a, int b, int c, int d, int e, int f)
1106 {
1107    int len = p->c->short_side_len;
1108    stbhw__draw_hline(p->data, p->stride, xpos+1      , ypos          , a, len, 0);
1109    stbhw__draw_vline(p->data, p->stride, xpos        , ypos+1        , b, len, 5);
1110    stbhw__draw_vline(p->data, p->stride, xpos + len+1, ypos+1        , c, len, 1);
1111    stbhw__draw_vline(p->data, p->stride, xpos        , ypos +   len+1, d, len, 4);
1112    stbhw__draw_vline(p->data, p->stride, xpos + len+1, ypos +   len+1, e, len, 5);
1113    stbhw__draw_hline(p->data, p->stride, xpos+1      , ypos + 2*len+1, f, len, 3);
1114 }
1115 
stbhw__corner_process_h_rect(stbhw__process * p,int xpos,int ypos,int a,int b,int c,int d,int e,int f)1116 static void stbhw__corner_process_h_rect(stbhw__process *p, int xpos, int ypos,
1117                             int a, int b, int c, int d, int e, int f)
1118 {
1119    int len = p->c->short_side_len;
1120 
1121    stbhw__draw_hline(p->data, p->stride, xpos+1        , ypos        , stbhw__c2e[a][b], len, 2);
1122    stbhw__draw_hline(p->data, p->stride, xpos+  len+1  , ypos        , stbhw__c2e[b][c], len, 3);
1123    stbhw__draw_vline(p->data, p->stride, xpos          , ypos+1      , stbhw__c2e[a][d], len, 1);
1124    stbhw__draw_vline(p->data, p->stride, xpos+2*len+1  , ypos+1      , stbhw__c2e[c][f], len, 4);
1125    stbhw__draw_hline(p->data, p->stride, xpos+1        , ypos + len+1, stbhw__c2e[d][e], len, 0);
1126    stbhw__draw_hline(p->data, p->stride, xpos + len+1  , ypos + len+1, stbhw__c2e[e][f], len, 2);
1127 
1128    if (p->c->corner_type_color_template[1][a]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len*2,len, 1,1);
1129    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);
1130    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 
1132    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);
1133    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);
1134    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 
1136    stbhw__set_pixel(p->data, p->stride, xpos        , ypos, stbhw__corner_colors[1][a]);
1137    stbhw__set_pixel(p->data, p->stride, xpos+len    , ypos, stbhw__corner_colors[2][b]);
1138    stbhw__set_pixel(p->data, p->stride, xpos+2*len+1, ypos, stbhw__corner_colors[3][c]);
1139    stbhw__set_pixel(p->data, p->stride, xpos        , ypos+len+1, stbhw__corner_colors[0][d]);
1140    stbhw__set_pixel(p->data, p->stride, xpos+len    , ypos+len+1, stbhw__corner_colors[1][e]);
1141    stbhw__set_pixel(p->data, p->stride, xpos+2*len+1, ypos+len+1, stbhw__corner_colors[2][f]);
1142 }
1143 
stbhw__corner_process_v_rect(stbhw__process * p,int xpos,int ypos,int a,int b,int c,int d,int e,int f)1144 static void stbhw__corner_process_v_rect(stbhw__process *p, int xpos, int ypos,
1145                             int a, int b, int c, int d, int e, int f)
1146 {
1147    int len = p->c->short_side_len;
1148 
1149    stbhw__draw_hline(p->data, p->stride, xpos+1      , ypos          , stbhw__c2e[a][d], len, 0);
1150    stbhw__draw_vline(p->data, p->stride, xpos        , ypos+1        , stbhw__c2e[a][b], len, 5);
1151    stbhw__draw_vline(p->data, p->stride, xpos + len+1, ypos+1        , stbhw__c2e[d][e], len, 1);
1152    stbhw__draw_vline(p->data, p->stride, xpos        , ypos +   len+1, stbhw__c2e[b][c], len, 4);
1153    stbhw__draw_vline(p->data, p->stride, xpos + len+1, ypos +   len+1, stbhw__c2e[e][f], len, 5);
1154    stbhw__draw_hline(p->data, p->stride, xpos+1      , ypos + 2*len+1, stbhw__c2e[c][f], len, 3);
1155 
1156    if (p->c->corner_type_color_template[0][a]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len,len*2, 1,1);
1157    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);
1158    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 
1160    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);
1161    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);
1162    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 
1164    stbhw__set_pixel(p->data, p->stride, xpos      , ypos        , stbhw__corner_colors[0][a]);
1165    stbhw__set_pixel(p->data, p->stride, xpos      , ypos+len    , stbhw__corner_colors[3][b]);
1166    stbhw__set_pixel(p->data, p->stride, xpos      , ypos+2*len+1, stbhw__corner_colors[2][c]);
1167    stbhw__set_pixel(p->data, p->stride, xpos+len+1, ypos        , stbhw__corner_colors[1][d]);
1168    stbhw__set_pixel(p->data, p->stride, xpos+len+1, ypos+len    , stbhw__corner_colors[0][e]);
1169    stbhw__set_pixel(p->data, p->stride, xpos+len+1, ypos+2*len+1, stbhw__corner_colors[3][f]);
1170 }
1171 
1172 // 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)1173 STBHW_EXTERN int stbhw_make_template(stbhw_config *c, unsigned char *data, int w, int h, int stride_in_bytes)
1174 {
1175    stbhw__process p;
1176    int i;
1177 
1178    p.data = data;
1179    p.w = w;
1180    p.h = h;
1181    p.stride = stride_in_bytes;
1182    p.ts = 0;
1183    p.c = c;
1184 
1185    if (c->is_corner) {
1186       p.process_h_rect = stbhw__corner_process_h_rect;
1187       p.process_v_rect = stbhw__corner_process_v_rect;
1188    } else {
1189       p.process_h_rect = stbhw__edge_process_h_rect;
1190       p.process_v_rect = stbhw__edge_process_v_rect;
1191    }
1192 
1193    for (i=0; i < p.h; ++i)
1194       memset(p.data + i*p.stride, 255, 3*p.w);
1195 
1196    if (!stbhw__process_template(&p))
1197       return 0;
1198 
1199    if (c->is_corner) {
1200       // write out binary information in first line of image
1201       for (i=0; i < 4; ++i)
1202          data[w*3-1-i] = c->num_color[i];
1203       data[w*3-1-i] = c->num_vary_x;
1204       data[w*3-2-i] = c->num_vary_y;
1205       data[w*3-3-i] = c->short_side_len;
1206       data[w*3-4-i] = 0xc0;
1207    } else {
1208       for (i=0; i < 6; ++i)
1209          data[w*3-1-i] = c->num_color[i];
1210       data[w*3-1-i] = c->num_vary_x;
1211       data[w*3-2-i] = c->num_vary_y;
1212       data[w*3-3-i] = c->short_side_len;
1213    }
1214 
1215    // make it more obvious it encodes actual data
1216    for (i=0; i < 9; ++i)
1217       p.data[p.w*3 - 1 - i] ^= i*55;
1218 
1219    return 1;
1220 }
1221 #endif // STB_HBWANG_IMPLEMENTATION
1222