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