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