summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2022-02-09 15:50:02 -0500
committerpommicket <pommicket@gmail.com>2022-02-09 15:50:02 -0500
commitd74dc53b0b07e5e29cf5db744342a73826779325 (patch)
tree6bde7f57e42b6a6848da08b5c85b118b6eb8f7f3
parentc548b12bbb500397b9f18e15af5589d89f25cfe2 (diff)
more parsing tests, fixed more bugs
- we now handle incomplete structs properly - small fix in for loop printing
-rw-r--r--05/constants.b2
-rw-r--r--05/idents.b28
-rw-r--r--05/main.b2
-rw-r--r--05/main.c2
-rw-r--r--05/parse.b77
-rw-r--r--05/tests/parse_stb_tilemap_editor.h3956
-rw-r--r--05/tests/parse_stb_truetype.h6
7 files changed, 4049 insertions, 24 deletions
diff --git a/05/constants.b b/05/constants.b
index 526a279..68e3777 100644
--- a/05/constants.b
+++ b/05/constants.b
@@ -244,7 +244,7 @@
; reading the first 16 bits of type data as a word will give this if the type refers to a function pointer.
#define TYPE2_FUNCTION_POINTER 0x100d
-; types willl be initialized (in main) so that these will refer to the proper types
+; types will be initialized (in main) so that these will refer to the proper types
#define TYPE_POINTER_TO_CHAR 20
#define TYPE_POINTER_TO_VOID 22
diff --git a/05/idents.b b/05/idents.b
index 11155d7..9663bf0 100644
--- a/05/idents.b
+++ b/05/idents.b
@@ -85,6 +85,34 @@ function ident_list_lookup
list -= 8 ; backtrack to value
return *8list ; UNALIGNED
+; return a pointer to the value associated with ident, or 0 if none is
+function ident_list_lookup_pointer
+ argument list
+ argument ident
+ local b
+ :ilist_lookup_pointer_loop
+ if *1list == 0 goto return_0
+ b = str_equals(list, ident)
+ list = memchr(list, 0)
+ list += 9 ; skip null byte and value
+ if b == 0 goto ilist_lookup_pointer_loop
+ list -= 8 ; backtrack to value
+ return list
+
+; if list contains ident, change its value to `value`. otherwise, add ident with the given value.
+function ident_list_set
+ argument list
+ argument ident
+ argument value
+ local p
+ p = ident_list_lookup_pointer(list, ident)
+ if p == 0 goto ident_list_set_add
+ *8p = value
+ return
+ :ident_list_set_add
+ ident_list_add(list, ident, value)
+ return
+
; if identifier in list, sets *pvalue to its value (if pvalue is not null) and returns 1
; otherwise, returns 0
function ident_list_lookup_check
diff --git a/05/main.b b/05/main.b
index 8074bf5..aa20095 100644
--- a/05/main.b
+++ b/05/main.b
@@ -72,6 +72,8 @@ global expressions_end
; current rbp offset (where rsp is)
global local_var_rbp_offset
global function_param_names
+; set to 1 by parse_type_declarators if at least one function parameter has no name.
+global function_param_has_no_name
#include util.b
#include idents.b
diff --git a/05/main.c b/05/main.c
index a630ef7..b7e07be 100644
--- a/05/main.c
+++ b/05/main.c
@@ -1,4 +1,4 @@
-/* #include "tests/parse_stb_truetype.h" */
+#include "tests/parse_stb_truetype.h"
/*
; @NONSTANDARD:
diff --git a/05/parse.b b/05/parse.b
index a643751..c5cd91e 100644
--- a/05/parse.b
+++ b/05/parse.b
@@ -240,6 +240,7 @@ function parse_toplevel_declaration
byte 0
:parse_function_definition
if block_depth != 0 goto nested_function
+ if function_param_has_no_name != 0 goto function_no_param_name
p = types + type
if *1p != TYPE_FUNCTION goto lbrace_after_declaration
@@ -280,6 +281,11 @@ function parse_toplevel_declaration
print_statement(out0)
goto parse_tld_ret
+ :function_no_param_name
+ token_error(base_type, .str_function_no_param_name)
+ :str_function_no_param_name
+ string Function definition with unnamed parameters.
+ byte 0
:blockdepth_internal_err
token_error(token, .str_blockdepth_internal_err)
:str_blockdepth_internal_err
@@ -660,6 +666,17 @@ function parse_statement
parse_type_declarators(l_prefix, l_prefix_end, l_suffix, l_suffix_end, 0)
parse_base_type(l_base_type)
+ ; create pseudo-entry for variable in local variables list.
+ ; this allows for int *x = malloc(sizeof *x);
+ ; unfortunately, it also allows int x = x;
+ ; oh well
+ p = local_variables
+ p += block_depth < 3
+ c = ident_list_lookup(*8p, l_name)
+ if c != 0 goto local_redeclaration
+ c = l_type < 32
+ ident_list_add(*8p, l_name, c)
+
token = l_suffix_end
if *1token == SYMBOL_EQ goto local_decl_initializer
@@ -682,11 +699,9 @@ function parse_statement
p = local_variables
p += block_depth < 3
l_offset = local_var_rbp_offset
- c = ident_list_lookup(*8p, l_name)
- if c != 0 goto local_redeclaration
c = l_offset
c |= l_type < 32
- ident_list_add(*8p, l_name, c)
+ ident_list_set(*8p, l_name, c)
if *1token == SYMBOL_SEMICOLON goto local_decl_loop_end
if *1token != SYMBOL_COMMA goto local_decl_badsuffix
@@ -989,7 +1004,7 @@ function print_statement_with_depth
print_expression(dat2)
:print_for_noexpr2
puts(.str_for_sep)
- if dat2 == 0 goto print_for_noexpr3
+ if dat3 == 0 goto print_for_noexpr3
print_expression(dat3)
:print_for_noexpr3
putcln(41)
@@ -1415,7 +1430,7 @@ function parse_constant_initializer
:floating_initializer_other_than_0
token_error(token, .str_floating_initializer_other_than_0)
:str_floating_initializer_other_than_0
- string Only 0 is supported as a floating-point initializer.
+ string Only 0 is supported as a constant floating-point initializer.
byte 0
; *p_token should be pointing to a {, this will advance it to point to the matching }
function token_skip_to_matching_rbrace
@@ -1825,6 +1840,13 @@ function parse_type_declarators
local param_suffix_end
local d
+ if param_names_out == 0 goto skip_ftype_reset
+ ; this gets set to 1 if at least one parameter has no name.
+ ; we don't just error here, because we need to support declarations like:
+ ; int f(int, int);
+ function_param_has_no_name = 0
+ :skip_ftype_reset
+
out = types + types_bytes_used
*1out = TYPE_FUNCTION
types_bytes_used += 1
@@ -1898,10 +1920,8 @@ function parse_type_declarators
suffix = p + 16
goto type_declarators_loop
:no_param_name
- token_error(param_suffix, .str_no_param_name)
- :str_no_param_name
- string Function parameter has no name.
- byte 0
+ function_param_has_no_name = 1
+ goto functype_had_ident
:stuff_after_ftype_varargs
token_error(p, .str_stuff_after_ftype_varargs)
:str_stuff_after_ftype_varargs
@@ -2080,18 +2100,21 @@ function parse_base_type
c = ident_list_lookup(structures, struct_name)
if *1p == SYMBOL_LBRACE goto base_type_struct_definition
- if c == 0 goto base_type_incomplete_struct
- ; e.g. struct Foo x; where struct Foo has been defined
+ if c == 0 goto base_type_new_incomplete_struct
+ :base_type_named_struct
+ ; e.g. struct Foo x;
*1out = TYPE_STRUCT
out += 1
*8out = c
out += 8
goto base_type_done
- :base_type_incomplete_struct
- ; e.g. struct Foo *x; where struct Foo hasn't been defined
- *1out = TYPE_VOID
- out += 1
- goto base_type_done
+ :base_type_new_incomplete_struct
+ ; create an ident list for the incomplete struct, with nothing in it yet
+ struct = ident_list_create(8000)
+ ; add it to the table
+ ident_list_add(structures, struct_name, struct)
+ c = struct
+ goto base_type_named_struct
:base_type_struct_definition
; @NONSTANDARD: we don't handle bit-fields.
local struct_location
@@ -2107,8 +2130,18 @@ function parse_base_type
struct_location = token_get_location(p)
- if c != 0 goto struct_maybe_redefinition
- struct = ident_list_create(8000) ; note: maximum "* 127 members in a single structure or union" C89 § 2.2.4.1
+ if c == 0 goto completely_new_struct
+ if *1c != 0 goto struct_maybe_redefinition
+ ; ok we're filling in an incomplete struct
+ struct = c
+ goto struct_definition_fill_in
+
+ :completely_new_struct
+ ; a completely new struct; hasn't been used as an incomplete struct
+ struct = ident_list_create(8000) ; note: maximum "* 127 members in a single structure or union" C89 § 2.2.4.1
+ ident_list_add(structures, struct_name, struct)
+
+ :struct_definition_fill_in
*1out = TYPE_STRUCT
out += 1
*8out = struct
@@ -2120,7 +2153,6 @@ function parse_base_type
offset = 0
if *1struct_name == 0 goto struct_unnamed
- ident_list_add(structures, struct_name, struct)
ident_list_add(structure_locations, struct_name, struct_location)
:struct_unnamed
@@ -2865,6 +2897,7 @@ function parse_expression
if *1a != TYPE_STRUCT goto member_non_struct
a += 1
a = *8a ; pointer to struct data
+ if *1a == 0 goto use_of_incomplete_struct
p += 8
c = ident_list_lookup(a, *8p)
if c == 0 goto member_not_in_struct
@@ -2874,6 +2907,11 @@ function parse_expression
p += 8
if p != tokens_end goto bad_expression ; e.g. foo->bar hello
return out
+ :use_of_incomplete_struct
+ token_error(p, .str_use_of_incomplete_struct)
+ :str_use_of_incomplete_struct
+ string Use of incomplete struct.
+ byte 0
:arrow_non_pointer
token_error(p, .str_arrow_non_pointer)
:str_arrow_non_pointer
@@ -2885,6 +2923,7 @@ function parse_expression
string Trying to access member of something other than a (complete) structure/union.
byte 0
:member_not_in_struct
+ p -= 8
token_error(p, .str_member_not_in_struct)
:str_member_not_in_struct
string Trying to access non-existent member of structure or union.
diff --git a/05/tests/parse_stb_tilemap_editor.h b/05/tests/parse_stb_tilemap_editor.h
new file mode 100644
index 0000000..3b04e06
--- /dev/null
+++ b/05/tests/parse_stb_tilemap_editor.h
@@ -0,0 +1,3956 @@
+#define va_arg(va, type) (*(type*)(va))
+#define va_start(va, arg)
+#define va_end(va)
+#define const
+#define signed
+#define volatile
+#define NULL ((void *)0)
+#define INT_MAX 0x7fffffff
+#define UINT_MAX 0xffffffffu
+typedef void *va_list;
+typedef unsigned long size_t;
+typedef long ptrdiff_t;
+
+#define STB_TILEMAP_EDITOR_IMPLEMENTATION
+#define STBTE_ASSERT(x) ((void)(x))
+#define STBTE_DRAW_RECT(a,b,c,d) ((void)(a,b,c,d))
+#define STBTE_DRAW_TILE(a,b,c,d,e) ((void)(a,b,c,d,e))
+
+void *memset(void *a, int c, size_t n) {
+ size_t i;
+ char *p = a;
+ for (i = 0; i < n; ++i)
+ *p++ = (char)c;
+ return a;
+}
+
+void *memcpy(void *dest, void *src, size_t n) {
+ char *p = dest, *q = src;
+ size_t i;
+ for (i = 0; i < n; ++i)
+ *p++ = *q++;
+ return dest;
+}
+
+int abs(int x) {
+ if (x >= 0)
+ return x;
+ return -x;
+}
+
+int strcmp(char *a, char *b) {
+ return 0;
+}
+
+int strncmp(char *a, char *b, size_t n) {
+ return 0;
+}
+
+double pow(double a, double b) {
+ return 0;
+}
+
+double ldexp(double x, int exp) {
+ return 0;
+}
+
+long strtol(const char *nptr, char **endptr, int base) {
+ return 0;
+}
+
+double sqrt(double x) {
+ return 0;
+}
+
+double floor(double x) {
+ return 0;
+}
+
+double ceil(double x) {
+ return 0;
+}
+
+double fmod(double x, double y) {
+ return 0;
+}
+
+double cos(double x) {
+ return 0;
+}
+
+double acos(double x) {
+ return 0;
+}
+
+double fabs(double x) {
+ return 0;
+}
+
+void *malloc(size_t n) {
+ return 0;
+}
+
+void free(void *mem) {
+}
+
+
+size_t strlen(char *s) {
+ return 0;
+}
+
+int sprintf(char *s, const char *fmt, ...) {
+ return 0;
+}
+
+#ifndef STB_TILEMAP_INCLUDE_STB_TILEMAP_EDITOR_H
+#define STB_TILEMAP_INCLUDE_STB_TILEMAP_EDITOR_H
+
+#ifdef _WIN32
+ #ifndef _CRT_SECURE_NO_WARNINGS
+ #define _CRT_SECURE_NO_WARNINGS
+ #endif
+#endif
+
+typedef struct stbte_tilemap stbte_tilemap;
+
+
+enum
+{
+ STBTE_drawmode_deemphasize = -1,
+ STBTE_drawmode_normal = 0,
+ STBTE_drawmode_emphasize = 1,
+};
+
+
+#define STBTE_PROP_none 0
+#define STBTE_PROP_int 1
+#define STBTE_PROP_float 2
+#define STBTE_PROP_bool 3
+#define STBTE_PROP_disabled 4
+
+
+
+
+
+
+extern stbte_tilemap *stbte_create_map(int map_x, int map_y, int map_layers, int spacing_x, int spacing_y, int max_tiles);
+
+
+
+
+
+
+
+
+
+
+extern void stbte_define_tile(stbte_tilemap *tm, unsigned short id, unsigned int layermask, const char * category);
+
+
+
+
+
+
+
+
+extern void stbte_set_display(int x0, int y0, int x1, int y1);
+
+
+
+
+
+
+
+
+extern void stbte_draw(stbte_tilemap *tm);
+
+extern void stbte_tick(stbte_tilemap *tm, float time_in_seconds_since_last_frame);
+
+
+
+
+
+
+
+
+extern void stbte_mouse_sdl(stbte_tilemap *tm, const void *sdl_event, float xscale, float yscale, int xoffset, int yoffset);
+
+
+extern void stbte_mouse_move(stbte_tilemap *tm, int x, int y, int shifted, int scrollkey);
+extern void stbte_mouse_button(stbte_tilemap *tm, int x, int y, int right, int down, int shifted, int scrollkey);
+extern void stbte_mouse_wheel(stbte_tilemap *tm, int x, int y, int vscroll);
+
+
+
+
+
+enum stbte_action
+{
+ STBTE_tool_select,
+ STBTE_tool_brush,
+ STBTE_tool_erase,
+ STBTE_tool_rectangle,
+ STBTE_tool_eyedropper,
+ STBTE_tool_link,
+ STBTE_act_toggle_grid,
+ STBTE_act_toggle_links,
+ STBTE_act_undo,
+ STBTE_act_redo,
+ STBTE_act_cut,
+ STBTE_act_copy,
+ STBTE_act_paste,
+ STBTE_scroll_left,
+ STBTE_scroll_right,
+ STBTE_scroll_up,
+ STBTE_scroll_down,
+};
+extern void stbte_action(stbte_tilemap *tm, enum stbte_action act);
+
+
+
+
+
+
+
+
+
+
+#define STBTE_EMPTY -1
+
+extern void stbte_get_dimensions(stbte_tilemap *tm, int *max_x, int *max_y);
+
+
+extern short* stbte_get_tile(stbte_tilemap *tm, int x, int y);
+
+
+
+extern float *stbte_get_properties(stbte_tilemap *tm, int x, int y);
+
+
+
+
+extern void stbte_get_link(stbte_tilemap *tm, int x, int y, int *destx, int *desty);
+
+
+extern void stbte_set_dimensions(stbte_tilemap *tm, int max_x, int max_y);
+
+
+
+extern void stbte_clear_map(stbte_tilemap *tm);
+
+
+
+extern void stbte_set_tile(stbte_tilemap *tm, int x, int y, int layer, signed short tile);
+
+
+extern void stbte_set_property(stbte_tilemap *tm, int x, int y, int n, float val);
+
+
+extern void stbte_set_link(stbte_tilemap *tm, int x, int y, int destx, int desty);
+
+
+
+
+
+
+
+
+extern void stbte_set_background_tile(stbte_tilemap *tm, short id);
+
+
+
+extern void stbte_set_sidewidths(int left, int right);
+
+
+
+extern void stbte_set_spacing(stbte_tilemap *tm, int spacing_x, int spacing_y, int palette_spacing_x, int palette_spacing_y);
+
+
+
+extern void stbte_set_layername(stbte_tilemap *tm, int layer, const char *layername);
+
+
+
+#endif
+
+#ifdef STB_TILEMAP_EDITOR_IMPLEMENTATION
+
+#ifndef STBTE_ASSERT
+#define STBTE_ASSERT assert
+#include <assert.h>
+#endif
+
+#ifdef _MSC_VER
+#define STBTE__NOTUSED(v) (void)(v)
+#else
+#define STBTE__NOTUSED(v) (void)sizeof(v)
+#endif
+
+#ifndef STBTE_MAX_TILEMAP_X
+#define STBTE_MAX_TILEMAP_X 200
+#endif
+
+#ifndef STBTE_MAX_TILEMAP_Y
+#define STBTE_MAX_TILEMAP_Y 200
+#endif
+
+#ifndef STBTE_MAX_LAYERS
+#define STBTE_MAX_LAYERS 8
+#endif
+
+#ifndef STBTE_MAX_CATEGORIES
+#define STBTE_MAX_CATEGORIES 100
+#endif
+
+#ifndef STBTE_MAX_COPY
+#define STBTE_MAX_COPY 65536
+#endif
+
+#ifndef STBTE_UNDO_BUFFER_BYTES
+#define STBTE_UNDO_BUFFER_BYTES (1 << 24)
+#endif
+
+#ifndef STBTE_PROP_TYPE
+#define STBTE__NO_PROPS
+#define STBTE_PROP_TYPE(n,td,tp) 0
+#endif
+
+#ifndef STBTE_PROP_NAME
+#define STBTE_PROP_NAME(n,td,tp) ""
+#endif
+
+#ifndef STBTE_MAX_PROPERTIES
+#define STBTE_MAX_PROPERTIES 10
+#endif
+
+#ifndef STBTE_PROP_MIN
+#define STBTE_PROP_MIN(n,td,tp) 0
+#endif
+
+#ifndef STBTE_PROP_MAX
+#define STBTE_PROP_MAX(n,td,tp) 100.0
+#endif
+
+#ifndef STBTE_PROP_FLOAT_SCALE
+#define STBTE_PROP_FLOAT_SCALE(n,td,tp) 1
+#endif
+
+#ifndef STBTE_FLOAT_CONTROL_GRANULARITY
+#define STBTE_FLOAT_CONTROL_GRANULARITY 4
+#endif
+
+
+#define STBTE__UNDO_BUFFER_COUNT (STBTE_UNDO_BUFFER_BYTES>>1)
+
+#if STBTE_MAX_TILEMAP_X > 4096 || STBTE_MAX_TILEMAP_Y > 4096
+#error "Maximum editable map size is 4096 x 4096"
+#endif
+#if STBTE_MAX_LAYERS > 32
+#error "Maximum layers allowed is 32"
+#endif
+#if STBTE_UNDO_BUFFER_COUNT & (STBTE_UNDO_BUFFER_COUNT-1)
+#error "Undo buffer size must be a power of 2"
+#endif
+
+#if STBTE_MAX_PROPERTIES == 0
+#define STBTE__NO_PROPS
+#endif
+
+#ifdef STBTE__NO_PROPS
+#undef STBTE_MAX_PROPERTIES
+#define STBTE_MAX_PROPERTIES 1
+#endif
+
+typedef struct
+{
+ short x,y;
+} stbte__link;
+
+enum
+{
+ STBTE__base,
+ STBTE__outline,
+ STBTE__text,
+
+ STBTE__num_color_aspects,
+};
+
+enum
+{
+ STBTE__idle,
+ STBTE__over,
+ STBTE__down,
+ STBTE__over_down,
+ STBTE__selected,
+ STBTE__selected_over,
+ STBTE__disabled,
+ STBTE__num_color_states,
+};
+
+enum
+{
+ STBTE__cexpander,
+ STBTE__ctoolbar,
+ STBTE__ctoolbar_button,
+ STBTE__cpanel,
+ STBTE__cpanel_sider,
+ STBTE__cpanel_sizer,
+ STBTE__cscrollbar,
+ STBTE__cmapsize,
+ STBTE__clayer_button,
+ STBTE__clayer_hide,
+ STBTE__clayer_lock,
+ STBTE__clayer_solo,
+ STBTE__ccategory_button,
+
+ STBTE__num_color_modes,
+};
+
+#ifdef STBTE__COLORPICKER
+static char *stbte__color_names[] =
+{
+ "expander", "toolbar", "tool button", "panel",
+ "panel c1", "panel c2", "scollbar", "map button",
+ "layer", "hide", "lock", "solo",
+ "category",
+};
+#endif
+
+
+static int stbte__color_table[STBTE__num_color_modes][STBTE__num_color_aspects][STBTE__num_color_states] =
+{
+ {
+ { 0x000000, 0x84987c, 0xdcdca8, 0xdcdca8, 0x40c040, 0x60d060, 0x505050 },
+ { 0xa4b090, 0xe0ec80, 0xffffc0, 0xffffc0, 0x80ff80, 0x80ff80, 0x606060 },
+ { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090 }
+ }, {
+ { 0x808890, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060 },
+ { 0x605860, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060 },
+ { 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000 }
+ }, {
+ { 0x3c5068, 0x7088a8, 0x647488, 0x94b4dc, 0x8890c4, 0x9caccc, 0x404040 },
+ { 0x889cb8, 0x889cb8, 0x889cb8, 0x889cb8, 0x84c4e8, 0xacc8ff, 0x0c0c08 },
+ { 0xbcc4cc, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x707074 }
+ }, {
+ { 0x403848, 0x403010, 0x403010, 0x403010, 0x403010, 0x403010, 0x303024 },
+ { 0x68546c, 0xc08040, 0xc08040, 0xc08040, 0xc08040, 0xc08040, 0x605030 },
+ { 0xf4e4ff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090 }
+ }, {
+ { 0xb4b04c, 0xacac60, 0xc0ffc0, 0xc0ffc0, 0x40c040, 0x60d060, 0x505050 },
+ { 0xa0a04c, 0xd0d04c, 0xffff80, 0xffff80, 0x80ff80, 0x80ff80, 0x606060 },
+ { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090 }
+ }, {
+ { 0x40c440, 0x60d060, 0xc0ffc0, 0xc0ffc0, 0x40c040, 0x60d060, 0x505050 },
+ { 0x40c040, 0x80ff80, 0x80ff80, 0x80ff80, 0x80ff80, 0x80ff80, 0x606060 },
+ { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090 }
+ }, {
+ { 0x9090ac, 0xa0a0b8, 0xbcb8cc, 0xbcb8cc, 0x909040, 0x909040, 0x909040 },
+ { 0xa0a0b8, 0xb0b4d0, 0xa0a0b8, 0xa0a0b8, 0xa0a050, 0xa0a050, 0xa0a050 },
+ { 0x808088, 0x808030, 0x808030, 0x808030, 0x808030, 0x808030, 0x808030 }
+ }, {
+ { 0x704c70, 0x885c8c, 0x9c68a4, 0xb870bc, 0xb490bc, 0xb490bc, 0x302828 },
+ { 0x646064, 0xcca8d4, 0xc060c0, 0xa07898, 0xe0b8e0, 0xe0b8e0, 0x403838 },
+ { 0xdccce4, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090 }
+ }, {
+ { 0x704c70, 0x885c8c, 0x9c68a4, 0xb870bc, 0xb490bc, 0xb490bc, 0x302828 },
+ { 0xb09cb4, 0xcca8d4, 0xc060c0, 0xa07898, 0xe0b8e0, 0xe0b8e0, 0x403838 },
+ { 0xdccce4, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090 }
+ }, {
+ { 0x646494, 0x888cb8, 0xb0b0b0, 0xb0b0cc, 0x9c9cf4, 0x8888b0, 0x50506c },
+ { 0x9090a4, 0xb0b4d4, 0xb0b0dc, 0xb0b0cc, 0xd0d0fc, 0xd0d4f0, 0x606060 },
+ { 0xb4b4d4, 0xe4e4ff, 0xffffff, 0xffffff, 0xe0e4ff, 0xececff, 0x909090 }
+ }, {
+ { 0x646444, 0x888c64, 0xb0b0b0, 0xb0b088, 0xaca858, 0x88886c, 0x505050 },
+ { 0x88886c, 0xb0b490, 0xb0b0b0, 0xb0b088, 0xd8d898, 0xd0d4b0, 0x606060 },
+ { 0xb4b49c, 0xffffd8, 0xffffff, 0xffffd4, 0xffffdc, 0xffffcc, 0x909090 }
+ }, {
+ { 0x906464, 0xb48c8c, 0xd4b0b0, 0xdcb0b0, 0xff9c9c, 0xc88888, 0x505050 },
+ { 0xb47c80, 0xd4b4b8, 0xc4a8a8, 0xdcb0b0, 0xffc0c0, 0xfce8ec, 0x606060 },
+ { 0xe0b4b4, 0xffdcd8, 0xffd8d4, 0xffe0e4, 0xffece8, 0xffffff, 0x909090 }
+ }, {
+ { 0x403848, 0x403848, 0x403848, 0x886894, 0x7c80c8, 0x7c80c8, 0x302828 },
+ { 0x403848, 0x403848, 0x403848, 0x403848, 0x7c80c8, 0x7c80c8, 0x403838 },
+ { 0xc8c4c8, 0xffffff, 0xffffff, 0xffffff, 0xe8e8ec, 0xffffff, 0x909090 }
+ }
+};
+
+#define STBTE_COLOR_TILEMAP_BACKGROUND 0x000000
+#define STBTE_COLOR_TILEMAP_BORDER 0x203060
+#define STBTE_COLOR_TILEMAP_HIGHLIGHT 0xffffff
+#define STBTE_COLOR_GRID 0x404040
+#define STBTE_COLOR_SELECTION_OUTLINE1 0xdfdfdf
+#define STBTE_COLOR_SELECTION_OUTLINE2 0x303030
+#define STBTE_COLOR_TILEPALETTE_OUTLINE 0xffffff
+#define STBTE_COLOR_TILEPALETTE_BACKGROUND 0x000000
+
+#ifndef STBTE_LINK_COLOR
+#define STBTE_LINK_COLOR(src,sp,dest,dp) 0x5030ff
+#endif
+
+#ifndef STBTE_LINK_COLOR_DRAWING
+#define STBTE_LINK_COLOR_DRAWING 0xff40ff
+#endif
+
+#ifndef STBTE_LINK_COLOR_DISALLOWED
+#define STBTE_LINK_COLOR_DISALLOWED 0x602060
+#endif
+
+
+
+static unsigned char stbte__state_to_index[2][2][2][2] =
+{
+ {
+ { { STBTE__idle , STBTE__over }, { STBTE__down , STBTE__over_down } },
+ { { STBTE__selected, STBTE__selected_over }, { STBTE__down , STBTE__over_down } }
+ },{
+ { { STBTE__disabled, STBTE__disabled }, { STBTE__disabled, STBTE__disabled } },
+ { { STBTE__selected, STBTE__selected_over }, { STBTE__disabled, STBTE__disabled } }
+ }
+};
+#define STBTE__INDEX_FOR_STATE(disable,select,down,over) stbte__state_to_index[disable][select][down][over]
+#define STBTE__INDEX_FOR_ID(id,disable,select) STBTE__INDEX_FOR_STATE(disable,select,STBTE__IS_ACTIVE(id),STBTE__IS_HOT(id))
+
+#define STBTE__FONT_HEIGHT 9
+static short stbte__font_offset[95+16];
+static short stbte__fontdata[769] =
+{
+ 4,9,6,9,9,9,9,8,9,8,4,9,7,7,7,7,4,2,6,8,6,6,7,3,4,4,8,6,3,6,2,6,6,6,6,6,6,
+ 6,6,6,6,6,2,3,5,4,5,6,6,6,6,6,6,6,6,6,6,6,6,7,6,7,7,7,6,7,6,6,6,6,7,7,6,6,
+ 6,4,6,4,7,7,3,6,6,5,6,6,5,6,6,4,5,6,4,7,6,6,6,6,6,6,6,6,6,7,6,6,6,5,2,5,8,
+ 0,0,0,0,2,253,130,456,156,8,72,184,64,2,125,66,64,160,64,146,511,146,146,
+ 511,146,146,511,146,511,257,341,297,341,297,341,257,511,16,56,124,16,16,16,
+ 124,56,16,96,144,270,261,262,136,80,48,224,192,160,80,40,22,14,15,3,448,496,
+ 496,240,232,20,10,5,2,112,232,452,450,225,113,58,28,63,30,60,200,455,257,
+ 257,0,0,0,257,257,455,120,204,132,132,159,14,4,4,14,159,132,132,204,120,8,
+ 24,56,120,56,24,8,32,48,56,60,56,48,32,0,0,0,0,111,111,7,7,0,0,7,7,34,127,
+ 127,34,34,127,127,34,36,46,107,107,58,18,99,51,24,12,102,99,48,122,79,93,
+ 55,114,80,4,7,3,62,127,99,65,65,99,127,62,8,42,62,28,28,62,42,8,8,8,62,62,
+ 8,8,128,224,96,8,8,8,8,8,8,96,96,96,48,24,12,6,3,62,127,89,77,127,62,64,66,
+ 127,127,64,64,98,115,89,77,71,66,33,97,73,93,119,35,24,28,22,127,127,16,39,
+ 103,69,69,125,57,62,127,73,73,121,48,1,1,113,121,15,7,54,127,73,73,127,54,
+ 6,79,73,105,63,30,54,54,128,246,118,8,28,54,99,65,20,20,20,20,65,99,54,28,
+ 8,2,3,105,109,7,2,30,63,33,45,47,46,124,126,19,19,126,124,127,127,73,73,127,
+ 54,62,127,65,65,99,34,127,127,65,99,62,28,127,127,73,73,73,65,127,127,9,9,
+ 9,1,62,127,65,73,121,121,127,127,8,8,127,127,65,65,127,127,65,65,32,96,64,
+ 64,127,63,127,127,8,28,54,99,65,127,127,64,64,64,64,127,127,6,12,6,127,127,
+ 127,127,6,12,24,127,127,62,127,65,65,65,127,62,127,127,9,9,15,6,62,127,65,
+ 81,49,127,94,127,127,9,25,127,102,70,79,73,73,121,49,1,1,127,127,1,1,63,127,
+ 64,64,127,63,15,31,48,96,48,31,15,127,127,48,24,48,127,127,99,119,28,28,119,
+ 99,7,15,120,120,15,7,97,113,89,77,71,67,127,127,65,65,3,6,12,24,48,96,65,
+ 65,127,127,8,12,6,3,6,12,8,64,64,64,64,64,64,64,3,7,4,32,116,84,84,124,120,
+ 127,127,68,68,124,56,56,124,68,68,68,56,124,68,68,127,127,56,124,84,84,92,
+ 24,8,124,126,10,10,56,380,324,324,508,252,127,127,4,4,124,120,72,122,122,
+ 64,256,256,256,506,250,126,126,16,56,104,64,66,126,126,64,124,124,24,56,28,
+ 124,120,124,124,4,4,124,120,56,124,68,68,124,56,508,508,68,68,124,56,56,124,
+ 68,68,508,508,124,124,4,4,12,8,72,92,84,84,116,36,4,4,62,126,68,68,60,124,
+ 64,64,124,124,28,60,96,96,60,28,28,124,112,56,112,124,28,68,108,56,56,108,
+ 68,284,316,352,320,508,252,68,100,116,92,76,68,8,62,119,65,65,127,127,65,
+ 65,119,62,8,16,24,12,12,24,24,12,4
+};
+
+typedef struct
+{
+ short id;
+ unsigned short category_id;
+ char *category;
+ unsigned int layermask;
+} stbte__tileinfo;
+
+#define MAX_LAYERMASK (1 << (8*sizeof(unsigned int)))
+
+typedef short stbte__tiledata;
+
+#define STBTE__NO_TILE -1
+
+enum
+{
+ STBTE__panel_toolbar,
+ STBTE__panel_colorpick,
+ STBTE__panel_info,
+ STBTE__panel_layers,
+ STBTE__panel_props,
+ STBTE__panel_categories,
+ STBTE__panel_tiles,
+
+ STBTE__num_panel,
+};
+
+enum
+{
+ STBTE__side_left,
+ STBTE__side_right,
+ STBTE__side_top,
+ STBTE__side_bottom,
+};
+
+enum
+{
+ STBTE__tool_select,
+ STBTE__tool_brush,
+ STBTE__tool_erase,
+ STBTE__tool_rect,
+ STBTE__tool_eyedrop,
+ STBTE__tool_fill,
+ STBTE__tool_link,
+
+ STBTE__tool_showgrid,
+ STBTE__tool_showlinks,
+
+ STBTE__tool_undo,
+ STBTE__tool_redo,
+
+
+ STBTE__num_tool,
+};
+
+
+static int toolchar[] = { 26,24,25,20,23,22,18, 19,17, 29,28 };
+
+enum
+{
+ STBTE__propmode_default,
+ STBTE__propmode_always,
+ STBTE__propmode_never
+};
+
+enum
+{
+ STBTE__paint,
+
+
+ STBTE__tick,
+ STBTE__mousemove,
+ STBTE__mousewheel,
+ STBTE__leftdown,
+ STBTE__leftup,
+ STBTE__rightdown,
+ STBTE__rightup
+};
+
+typedef struct
+{
+ int expanded, mode;
+ int delta_height;
+ int side;
+ int width,height;
+ int x0,y0;
+} stbte__panel;
+
+typedef struct
+{
+ int x0,y0,x1,y1,color;
+} stbte__colorrect;
+
+#define STBTE__MAX_DELAYRECT 256
+
+typedef struct
+{
+ int tool, active_event;
+ int active_id, hot_id, next_hot_id;
+ int event;
+ int mx,my, dx,dy;
+ int ms_time;
+ int shift, scrollkey;
+ int initted;
+ int side_extended[2];
+ stbte__colorrect delayrect[STBTE__MAX_DELAYRECT];
+ int delaycount;
+ int show_grid, show_links;
+ int brush_state;
+ int eyedrop_x, eyedrop_y, eyedrop_last_layer;
+ int pasting, paste_x, paste_y;
+ int scrolling, start_x, start_y;
+ int last_mouse_x, last_mouse_y;
+ int accum_x, accum_y;
+ int linking;
+ int dragging;
+ int drag_x, drag_y, drag_w, drag_h;
+ int drag_offx, drag_offy, drag_dest_x, drag_dest_y;
+ int undoing;
+ int has_selection, select_x0, select_y0, select_x1, select_y1;
+ int sx,sy;
+ int x0,y0,x1,y1, left_width, right_width;
+ float alert_timer;
+ const char *alert_msg;
+ float dt;
+ stbte__panel panel[STBTE__num_panel];
+ short copybuffer[STBTE_MAX_COPY][STBTE_MAX_LAYERS];
+ float copyprops[STBTE_MAX_COPY][STBTE_MAX_PROPERTIES];
+#ifdef STBTE_ALLOW_LINK
+ stbte__link copylinks[STBTE_MAX_COPY];
+#endif
+ int copy_src_x, copy_src_y;
+ stbte_tilemap *copy_src;
+ int copy_width,copy_height,has_copy,copy_has_props;
+} stbte__ui_t;
+
+
+static stbte__ui_t stbte__ui = { STBTE__tool_brush, 0 };
+
+#define STBTE__INACTIVE() (stbte__ui.active_id == 0)
+#define STBTE__IS_ACTIVE(id) (stbte__ui.active_id == (id))
+#define STBTE__IS_HOT(id) (stbte__ui.hot_id == (id))
+
+#define STBTE__BUTTON_HEIGHT (STBTE__FONT_HEIGHT + 2 * STBTE__BUTTON_INTERNAL_SPACING)
+#define STBTE__BUTTON_INTERNAL_SPACING (2 + (STBTE__FONT_HEIGHT>>4))
+
+typedef struct
+{
+ const char *name;
+ int locked;
+ int hidden;
+} stbte__layer;
+
+enum
+{
+ STBTE__unlocked,
+ STBTE__protected,
+ STBTE__locked,
+};
+
+struct stbte_tilemap
+{
+ stbte__tiledata data[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X][STBTE_MAX_LAYERS];
+ float props[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X][STBTE_MAX_PROPERTIES];
+ #ifdef STBTE_ALLOW_LINK
+ stbte__link link[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X];
+ int linkcount[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X];
+ #endif
+ int max_x, max_y, num_layers;
+ int spacing_x, spacing_y;
+ int palette_spacing_x, palette_spacing_y;
+ int scroll_x,scroll_y;
+ int cur_category, cur_tile, cur_layer;
+ char *categories[STBTE_MAX_CATEGORIES];
+ int num_categories, category_scroll;
+ stbte__tileinfo *tiles;
+ int num_tiles, max_tiles, digits;
+ unsigned char undo_available_valid;
+ unsigned char undo_available;
+ unsigned char redo_available;
+ unsigned char padding;
+ int cur_palette_count;
+ int palette_scroll;
+ int tileinfo_dirty;
+ stbte__layer layerinfo[STBTE_MAX_LAYERS];
+ int has_layer_names;
+ int layername_width;
+ int layer_scroll;
+ int propmode;
+ int solo_layer;
+ int undo_pos, undo_len, redo_len;
+ short background_tile;
+ unsigned char id_in_use[32768>>3];
+ short *undo_buffer;
+};
+
+static char *default_category = (char*) "[unassigned]";
+
+static void stbte__init_gui(void)
+{
+ int i,n;
+ stbte__ui.initted = 1;
+
+ stbte__ui.show_links = 1;
+ for (i=0; i < STBTE__num_panel; ++i) {
+ stbte__ui.panel[i].expanded = 1;
+ stbte__ui.panel[i].delta_height = 0;
+ stbte__ui.panel[i].side = STBTE__side_left;
+ }
+ stbte__ui.panel[STBTE__panel_toolbar ].side = STBTE__side_top;
+ stbte__ui.panel[STBTE__panel_colorpick].side = STBTE__side_right;
+
+ if (stbte__ui.left_width == 0)
+ stbte__ui.left_width = 80;
+ if (stbte__ui.right_width == 0)
+ stbte__ui.right_width = 80;
+
+
+ n=95+16;
+ for (i=0; i < 95+16; ++i) {
+ stbte__font_offset[i] = n;
+ n += stbte__fontdata[i];
+ }
+}
+
+stbte_tilemap *stbte_create_map(int map_x, int map_y, int map_layers, int spacing_x, int spacing_y, int max_tiles)
+{
+ int i;
+ stbte_tilemap *tm;
+ STBTE_ASSERT(map_layers >= 0 && map_layers <= STBTE_MAX_LAYERS);
+ STBTE_ASSERT(map_x >= 0 && map_x <= STBTE_MAX_TILEMAP_X);
+ STBTE_ASSERT(map_y >= 0 && map_y <= STBTE_MAX_TILEMAP_Y);
+ if (map_x < 0 || map_y < 0 || map_layers < 0 ||
+ map_x > STBTE_MAX_TILEMAP_X || map_y > STBTE_MAX_TILEMAP_Y || map_layers > STBTE_MAX_LAYERS)
+ return NULL;
+
+ if (!stbte__ui.initted)
+ stbte__init_gui();
+
+ tm = (stbte_tilemap *) malloc(sizeof(*tm) + sizeof(*tm->tiles) * max_tiles + STBTE_UNDO_BUFFER_BYTES);
+ if (tm == NULL)
+ return NULL;
+
+ tm->tiles = (stbte__tileinfo *) (tm+1);
+ tm->undo_buffer = (short *) (tm->tiles + max_tiles);
+ tm->num_layers = map_layers;
+ tm->max_x = map_x;
+ tm->max_y = map_y;
+ tm->spacing_x = spacing_x;
+ tm->spacing_y = spacing_y;
+ tm->scroll_x = 0;
+ tm->scroll_y = 0;
+ tm->palette_scroll = 0;
+ tm->palette_spacing_x = spacing_x+1;
+ tm->palette_spacing_y = spacing_y+1;
+ tm->cur_category = -1;
+ tm->cur_tile = 0;
+ tm->solo_layer = -1;
+ tm->undo_len = 0;
+ tm->redo_len = 0;
+ tm->undo_pos = 0;
+ tm->category_scroll = 0;
+ tm->layer_scroll = 0;
+ tm->propmode = 0;
+ tm->has_layer_names = 0;
+ tm->layername_width = 0;
+ tm->undo_available_valid = 0;
+
+ for (i=0; i < tm->num_layers; ++i) {
+ tm->layerinfo[i].hidden = 0;
+ tm->layerinfo[i].locked = STBTE__unlocked;
+ tm->layerinfo[i].name = 0;
+ }
+
+ tm->background_tile = STBTE__NO_TILE;
+ stbte_clear_map(tm);
+
+ tm->max_tiles = max_tiles;
+ tm->num_tiles = 0;
+ for (i=0; i < 32768/8; ++i)
+ tm->id_in_use[i] = 0;
+ tm->tileinfo_dirty = 1;
+ return tm;
+}
+
+void stbte_set_background_tile(stbte_tilemap *tm, short id)
+{
+ int i;
+ STBTE_ASSERT(id >= -1);
+
+ if (id < -1)
+ return;
+ for (i=0; i < STBTE_MAX_TILEMAP_X * STBTE_MAX_TILEMAP_Y; ++i)
+ if (tm->data[0][i][0] == -1)
+ tm->data[0][i][0] = id;
+ tm->background_tile = id;
+}
+
+void stbte_set_spacing(stbte_tilemap *tm, int spacing_x, int spacing_y, int palette_spacing_x, int palette_spacing_y)
+{
+ tm->spacing_x = spacing_x;
+ tm->spacing_y = spacing_y;
+ tm->palette_spacing_x = palette_spacing_x;
+ tm->palette_spacing_y = palette_spacing_y;
+}
+
+void stbte_set_sidewidths(int left, int right)
+{
+ stbte__ui.left_width = left;
+ stbte__ui.right_width = right;
+}
+
+void stbte_set_display(int x0, int y0, int x1, int y1)
+{
+ stbte__ui.x0 = x0;
+ stbte__ui.y0 = y0;
+ stbte__ui.x1 = x1;
+ stbte__ui.y1 = y1;
+}
+
+void stbte_define_tile(stbte_tilemap *tm, unsigned short id, unsigned int layermask, const char * category_c)
+{
+ char *category = (char *) category_c;
+ STBTE_ASSERT(id < 32768);
+ STBTE_ASSERT(tm->num_tiles < tm->max_tiles);
+ STBTE_ASSERT((tm->id_in_use[id>>3]&(1<<(id&7))) == 0);
+ if (id >= 32768 || tm->num_tiles >= tm->max_tiles || (tm->id_in_use[id>>3]&(1<<(id&7))))
+ return;
+
+ if (category == NULL)
+ category = (char*) default_category;
+ tm->id_in_use[id>>3] |= 1 << (id&7);
+ tm->tiles[tm->num_tiles].category = category;
+ tm->tiles[tm->num_tiles].id = id;
+ tm->tiles[tm->num_tiles].layermask = layermask;
+ ++tm->num_tiles;
+ tm->tileinfo_dirty = 1;
+}
+
+static int stbte__text_width(const char *str);
+
+void stbte_set_layername(stbte_tilemap *tm, int layer, const char *layername)
+{
+ STBTE_ASSERT(layer >= 0 && layer < tm->num_layers);
+ if (layer >= 0 && layer < tm->num_layers) {
+ int width;
+ tm->layerinfo[layer].name = layername;
+ tm->has_layer_names = 1;
+ width = stbte__text_width(layername);
+ tm->layername_width = (width > tm->layername_width ? width : tm->layername_width);
+ }
+}
+
+void stbte_get_dimensions(stbte_tilemap *tm, int *max_x, int *max_y)
+{
+ *max_x = tm->max_x;
+ *max_y = tm->max_y;
+}
+
+short* stbte_get_tile(stbte_tilemap *tm, int x, int y)
+{
+ STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y);
+ if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y)
+ return NULL;
+ return tm->data[y][x];
+}
+
+float *stbte_get_properties(stbte_tilemap *tm, int x, int y)
+{
+ STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y);
+ if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y)
+ return NULL;
+ return tm->props[y][x];
+}
+
+void stbte_get_link(stbte_tilemap *tm, int x, int y, int *destx, int *desty)
+{
+ int gx=-1,gy=-1;
+ STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y);
+#ifdef STBTE_ALLOW_LINK
+ if (x >= 0 && x < STBTE_MAX_TILEMAP_X && y >= 0 && y < STBTE_MAX_TILEMAP_Y) {
+ gx = tm->link[y][x].x;
+ gy = tm->link[y][x].y;
+ if (gx >= 0)
+ if (!STBTE_ALLOW_LINK(tm->data[y][x], tm->props[y][x], tm->data[gy][gx], tm->props[gy][gx]))
+ gx = gy = -1;
+ }
+#endif
+ *destx = gx;
+ *desty = gy;
+}
+
+void stbte_set_property(stbte_tilemap *tm, int x, int y, int n, float val)
+{
+ tm->props[y][x][n] = val;
+}
+
+#ifdef STBTE_ALLOW_LINK
+static void stbte__set_link(stbte_tilemap *tm, int src_x, int src_y, int dest_x, int dest_y, int undo_mode);
+#endif
+
+enum
+{
+ STBTE__undo_none,
+ STBTE__undo_record,
+ STBTE__undo_block,
+};
+
+void stbte_set_link(stbte_tilemap *tm, int x, int y, int destx, int desty)
+{
+#ifdef STBTE_ALLOW_LINK
+ stbte__set_link(tm, x, y, destx, desty, STBTE__undo_none);
+#else
+ STBTE_ASSERT(0);
+#endif
+}
+
+
+
+
+
+void stbte_set_dimensions(stbte_tilemap *tm, int map_x, int map_y)
+{
+ STBTE_ASSERT(map_x >= 0 && map_x <= STBTE_MAX_TILEMAP_X);
+ STBTE_ASSERT(map_y >= 0 && map_y <= STBTE_MAX_TILEMAP_Y);
+ if (map_x < 0 || map_y < 0 || map_x > STBTE_MAX_TILEMAP_X || map_y > STBTE_MAX_TILEMAP_Y)
+ return;
+ tm->max_x = map_x;
+ tm->max_y = map_y;
+}
+
+void stbte_clear_map(stbte_tilemap *tm)
+{
+ int i,j;
+ for (i=0; i < STBTE_MAX_TILEMAP_X * STBTE_MAX_TILEMAP_Y; ++i) {
+ tm->data[0][i][0] = tm->background_tile;
+ for (j=1; j < tm->num_layers; ++j)
+ tm->data[0][i][j] = STBTE__NO_TILE;
+ for (j=0; j < STBTE_MAX_PROPERTIES; ++j)
+ tm->props[0][i][j] = 0;
+ #ifdef STBTE_ALLOW_LINK
+ tm->link[0][i].x = -1;
+ tm->link[0][i].y = -1;
+ tm->linkcount[0][i] = 0;
+ #endif
+ }
+}
+
+void stbte_set_tile(stbte_tilemap *tm, int x, int y, int layer, signed short tile)
+{
+ STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y);
+ STBTE_ASSERT(layer >= 0 && layer < tm->num_layers);
+ STBTE_ASSERT(tile >= -1);
+
+ if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y)
+ return;
+ if (layer < 0 || layer >= tm->num_layers || tile < -1)
+ return;
+ tm->data[y][x][layer] = tile;
+}
+
+static void stbte__choose_category(stbte_tilemap *tm, int category)
+{
+ int i,n=0;
+ tm->cur_category = category;
+ for (i=0; i < tm->num_tiles; ++i)
+ if (tm->tiles[i].category_id == category || category == -1)
+ ++n;
+ tm->cur_palette_count = n;
+ tm->palette_scroll = 0;
+}
+
+static int stbte__strequal(char *p, char *q)
+{
+ while (*p)
+ if (*p++ != *q++) return 0;
+ return *q == 0;
+}
+
+static void stbte__compute_tileinfo(stbte_tilemap *tm)
+{
+ int i,j;
+
+ tm->num_categories=0;
+
+ for (i=0; i < tm->num_tiles; ++i) {
+ stbte__tileinfo *t = &tm->tiles[i];
+
+ for (j=0; j < tm->num_categories; ++j)
+ if (stbte__strequal(t->category, tm->categories[j]))
+ goto found;
+ tm->categories[j] = t->category;
+ ++tm->num_categories;
+ found:
+ t->category_id = (unsigned short) j;
+ }
+
+
+
+ if (tm->cur_category > tm->num_categories) {
+ tm->cur_category = -1;
+ }
+
+ stbte__choose_category(tm, tm->cur_category);
+
+ tm->tileinfo_dirty = 0;
+}
+
+static void stbte__prepare_tileinfo(stbte_tilemap *tm)
+{
+ if (tm->tileinfo_dirty)
+ stbte__compute_tileinfo(tm);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#define stbte__wrap(pos) ((pos) & (STBTE__UNDO_BUFFER_COUNT-1))
+
+#define STBTE__undo_record -2
+#define STBTE__redo_record -3
+#define STBTE__undo_junk -4
+
+static void stbte__write_undo(stbte_tilemap *tm, short value)
+{
+ int pos = tm->undo_pos;
+ tm->undo_buffer[pos] = value;
+ tm->undo_pos = stbte__wrap(pos+1);
+ tm->undo_len += (tm->undo_len < STBTE__UNDO_BUFFER_COUNT-2);
+ tm->redo_len -= (tm->redo_len > 0);
+ tm->undo_available_valid = 0;
+}
+
+static void stbte__write_redo(stbte_tilemap *tm, short value)
+{
+ int pos = tm->undo_pos;
+ tm->undo_buffer[pos] = value;
+ tm->undo_pos = stbte__wrap(pos-1);
+ tm->redo_len += (tm->redo_len < STBTE__UNDO_BUFFER_COUNT-2);
+ tm->undo_len -= (tm->undo_len > 0);
+ tm->undo_available_valid = 0;
+}
+
+static void stbte__begin_undo(stbte_tilemap *tm)
+{
+ tm->redo_len = 0;
+ stbte__write_undo(tm, STBTE__undo_record);
+ stbte__ui.undoing = 1;
+ stbte__ui.alert_msg = 0;
+}
+
+static void stbte__end_undo(stbte_tilemap *tm)
+{
+ if (stbte__ui.undoing) {
+
+ int pos = stbte__wrap(tm->undo_pos-1);
+ if (tm->undo_buffer[pos] == STBTE__undo_record) {
+
+ tm->undo_pos = pos;
+ STBTE_ASSERT(tm->undo_len > 0);
+ tm->undo_len -= 1;
+ }
+ tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk;
+
+
+ stbte__ui.undoing = 0;
+ }
+}
+
+static void stbte__undo_record(stbte_tilemap *tm, int x, int y, int i, int v)
+{
+ STBTE_ASSERT(stbte__ui.undoing);
+ if (stbte__ui.undoing) {
+ stbte__write_undo(tm, v);
+ stbte__write_undo(tm, x);
+ stbte__write_undo(tm, y);
+ stbte__write_undo(tm, i);
+ }
+}
+
+static void stbte__redo_record(stbte_tilemap *tm, int x, int y, int i, int v)
+{
+ stbte__write_redo(tm, v);
+ stbte__write_redo(tm, x);
+ stbte__write_redo(tm, y);
+ stbte__write_redo(tm, i);
+}
+
+static float stbte__extract_float(short s0, short s1)
+{
+ union { float f; short s[2]; } converter;
+ converter.s[0] = s0;
+ converter.s[1] = s1;
+ return converter.f;
+}
+
+static short stbte__extract_short(float f, int slot)
+{
+ union { float f; short s[2]; } converter;
+ converter.f = f;
+ return converter.s[slot];
+}
+
+static void stbte__undo_record_prop(stbte_tilemap *tm, int x, int y, int i, short s0, short s1)
+{
+ STBTE_ASSERT(stbte__ui.undoing);
+ if (stbte__ui.undoing) {
+ stbte__write_undo(tm, s1);
+ stbte__write_undo(tm, s0);
+ stbte__write_undo(tm, x);
+ stbte__write_undo(tm, y);
+ stbte__write_undo(tm, 256+i);
+ }
+}
+
+static void stbte__undo_record_prop_float(stbte_tilemap *tm, int x, int y, int i, float f)
+{
+ stbte__undo_record_prop(tm, x,y,i, stbte__extract_short(f,0), stbte__extract_short(f,1));
+}
+
+static void stbte__redo_record_prop(stbte_tilemap *tm, int x, int y, int i, short s0, short s1)
+{
+ stbte__write_redo(tm, s1);
+ stbte__write_redo(tm, s0);
+ stbte__write_redo(tm, x);
+ stbte__write_redo(tm, y);
+ stbte__write_redo(tm, 256+i);
+}
+
+
+static int stbte__undo_find_end(stbte_tilemap *tm)
+{
+
+ int i, pos = stbte__wrap(tm->undo_pos-1);
+ for (i=0; i < tm->undo_len;) {
+ STBTE_ASSERT(tm->undo_buffer[pos] != STBTE__undo_junk);
+ if (tm->undo_buffer[pos] == STBTE__undo_record)
+ break;
+ if (tm->undo_buffer[pos] >= 255)
+ pos = stbte__wrap(pos-5), i += 5;
+ else
+ pos = stbte__wrap(pos-4), i += 4;
+ }
+ if (i >= tm->undo_len)
+ return -1;
+ return pos;
+}
+
+static void stbte__undo(stbte_tilemap *tm)
+{
+ int i, pos, endpos;
+ endpos = stbte__undo_find_end(tm);
+ if (endpos < 0)
+ return;
+
+
+ pos = stbte__wrap(tm->undo_pos-1);
+
+
+ stbte__write_redo(tm, STBTE__redo_record);
+
+
+
+ for (i=0; endpos != pos; i += 4) {
+ int x,y,n,v;
+
+ n = tm->undo_buffer[pos];
+ y = tm->undo_buffer[stbte__wrap(pos-1)];
+ x = tm->undo_buffer[stbte__wrap(pos-2)];
+ v = tm->undo_buffer[stbte__wrap(pos-3)];
+ if (n >= 255) {
+ short s0=0,s1=0;
+ int v2 = tm->undo_buffer[stbte__wrap(pos-4)];
+ pos = stbte__wrap(pos-5);
+ if (n > 255) {
+ float vf = stbte__extract_float(v, v2);
+ s0 = stbte__extract_short(tm->props[y][x][n-256], 0);
+ s1 = stbte__extract_short(tm->props[y][x][n-256], 1);
+ tm->props[y][x][n-256] = vf;
+ } else {
+#ifdef STBTE_ALLOW_LINK
+ s0 = tm->link[y][x].x;
+ s1 = tm->link[y][x].y;
+ stbte__set_link(tm, x,y, v, v2, STBTE__undo_none);
+#endif
+ }
+
+ stbte__redo_record_prop(tm, x, y, n-256, s0,s1);
+
+ } else {
+ pos = stbte__wrap(pos-4);
+
+ stbte__redo_record(tm, x, y, n, tm->data[y][x][n]);
+
+ tm->data[y][x][n] = (short) v;
+ }
+ }
+
+ tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk;
+}
+
+static int stbte__redo_find_end(stbte_tilemap *tm)
+{
+
+ int i, pos = stbte__wrap(tm->undo_pos+1);
+ for (i=0; i < tm->redo_len;) {
+ STBTE_ASSERT(tm->undo_buffer[pos] != STBTE__undo_junk);
+ if (tm->undo_buffer[pos] == STBTE__redo_record)
+ break;
+ if (tm->undo_buffer[pos] >= 255)
+ pos = stbte__wrap(pos+5), i += 5;
+ else
+ pos = stbte__wrap(pos+4), i += 4;
+ }
+ if (i >= tm->redo_len)
+ return -1;
+ return pos;
+}
+
+static void stbte__redo(stbte_tilemap *tm)
+{
+
+ int i, pos, endpos;
+ endpos = stbte__redo_find_end(tm);
+ if (endpos < 0)
+ return;
+
+
+ pos = stbte__wrap(tm->undo_pos+1);
+
+
+ stbte__write_undo(tm, STBTE__undo_record);
+
+ for (i=0; pos != endpos; i += 4) {
+ int x,y,n,v;
+ n = tm->undo_buffer[pos];
+ y = tm->undo_buffer[stbte__wrap(pos+1)];
+ x = tm->undo_buffer[stbte__wrap(pos+2)];
+ v = tm->undo_buffer[stbte__wrap(pos+3)];
+ if (n >= 255) {
+ int v2 = tm->undo_buffer[stbte__wrap(pos+4)];
+ short s0=0,s1=0;
+ pos = stbte__wrap(pos+5);
+ if (n > 255) {
+ float vf = stbte__extract_float(v, v2);
+ s0 = stbte__extract_short(tm->props[y][x][n-256],0);
+ s1 = stbte__extract_short(tm->props[y][x][n-256],1);
+ tm->props[y][x][n-256] = vf;
+ } else {
+#ifdef STBTE_ALLOW_LINK
+ s0 = tm->link[y][x].x;
+ s1 = tm->link[y][x].y;
+ stbte__set_link(tm, x,y,v,v2, STBTE__undo_none);
+#endif
+ }
+
+ stbte__write_undo(tm, s1);
+ stbte__write_undo(tm, s0);
+ stbte__write_undo(tm, x);
+ stbte__write_undo(tm, y);
+ stbte__write_undo(tm, n);
+ } else {
+ pos = stbte__wrap(pos+4);
+
+ stbte__write_undo(tm, tm->data[y][x][n]);
+ stbte__write_undo(tm, x);
+ stbte__write_undo(tm, y);
+ stbte__write_undo(tm, n);
+ tm->data[y][x][n] = (short) v;
+ }
+ }
+ tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk;
+}
+
+
+static void stbte__recompute_undo_available(stbte_tilemap *tm)
+{
+ tm->undo_available = (stbte__undo_find_end(tm) >= 0);
+ tm->redo_available = (stbte__redo_find_end(tm) >= 0);
+}
+
+static int stbte__undo_available(stbte_tilemap *tm)
+{
+ if (!tm->undo_available_valid)
+ stbte__recompute_undo_available(tm);
+ return tm->undo_available;
+}
+
+static int stbte__redo_available(stbte_tilemap *tm)
+{
+ if (!tm->undo_available_valid)
+ stbte__recompute_undo_available(tm);
+ return tm->redo_available;
+}
+
+
+
+#ifdef STBTE_ALLOW_LINK
+static void stbte__set_link(stbte_tilemap *tm, int src_x, int src_y, int dest_x, int dest_y, int undo_mode)
+{
+ stbte__link *a;
+ STBTE_ASSERT(src_x >= 0 && src_x < STBTE_MAX_TILEMAP_X && src_y >= 0 && src_y < STBTE_MAX_TILEMAP_Y);
+ a = &tm->link[src_y][src_x];
+
+ if (a->x == dest_x && a->y == dest_y)
+ return;
+ if (undo_mode != STBTE__undo_none ) {
+ if (undo_mode == STBTE__undo_block) stbte__begin_undo(tm);
+ stbte__undo_record_prop(tm, src_x, src_y, -1, a->x, a->y);
+ if (undo_mode == STBTE__undo_block) stbte__end_undo(tm);
+ }
+
+ if (a->x >= 0) {
+
+ STBTE_ASSERT(tm->linkcount[a->y][a->x] > 0);
+ --tm->linkcount[a->y][a->x];
+ }
+
+ if (dest_x >= 0) {
+ ++tm->linkcount[dest_y][dest_x];
+ }
+ a->x = dest_x;
+ a->y = dest_y;
+}
+#endif
+
+
+static void stbte__draw_rect(int x0, int y0, int x1, int y1, unsigned int color)
+{
+ STBTE_DRAW_RECT(x0,y0,x1,y1, color);
+}
+
+#ifdef STBTE_ALLOW_LINK
+static void stbte__draw_line(int x0, int y0, int x1, int y1, unsigned int color)
+{
+ int temp;
+ if (x1 < x0) temp=x0,x0=x1,x1=temp;
+ if (y1 < y0) temp=y0,y0=y1,y1=temp;
+ stbte__draw_rect(x0,y0,x1+1,y1+1,color);
+}
+
+static void stbte__draw_link(int x0, int y0, int x1, int y1, unsigned int color)
+{
+ stbte__draw_line(x0,y0,x0,y1, color);
+ stbte__draw_line(x0,y1,x1,y1, color);
+}
+#endif
+
+static void stbte__draw_frame(int x0, int y0, int x1, int y1, unsigned int color)
+{
+ stbte__draw_rect(x0,y0,x1-1,y0+1,color);
+ stbte__draw_rect(x1-1,y0,x1,y1-1,color);
+ stbte__draw_rect(x0+1,y1-1,x1,y1,color);
+ stbte__draw_rect(x0,y0+1,x0+1,y1,color);
+}
+
+static int stbte__get_char_width(int ch)
+{
+ return stbte__fontdata[ch-16];
+}
+
+static short *stbte__get_char_bitmap(int ch)
+{
+ return stbte__fontdata + stbte__font_offset[ch-16];
+}
+
+static void stbte__draw_bitmask_as_columns(int x, int y, short bitmask, int color)
+{
+ int start_i = -1, i=0;
+ while (bitmask) {
+ if (bitmask & (1<<i)) {
+ if (start_i < 0)
+ start_i = i;
+ } else if (start_i >= 0) {
+ stbte__draw_rect(x, y+start_i, x+1, y+i, color);
+ start_i = -1;
+ bitmask &= ~((1<<i)-1);
+ }
+ ++i;
+ }
+}
+
+static void stbte__draw_bitmap(int x, int y, int w, short *bitmap, int color)
+{
+ int i;
+ for (i=0; i < w; ++i)
+ stbte__draw_bitmask_as_columns(x+i, y, *bitmap++, color);
+}
+
+static void stbte__draw_text_core(int x, int y, const char *str, int w, int color, int digitspace)
+{
+ int x_end = x+w;
+ while (*str) {
+ int c = *str++;
+ int cw = stbte__get_char_width(c);
+ if (x + cw > x_end)
+ break;
+ stbte__draw_bitmap(x, y, cw, stbte__get_char_bitmap(c), color);
+ if (digitspace && c == ' ')
+ cw = stbte__get_char_width('0');
+ x += cw+1;
+ }
+}
+
+static void stbte__draw_text(int x, int y, const char *str, int w, int color)
+{
+ stbte__draw_text_core(x,y,str,w,color,0);
+}
+
+static int stbte__text_width(const char *str)
+{
+ int x = 0;
+ while (*str) {
+ int c = *str++;
+ int cw = stbte__get_char_width(c);
+ x += cw+1;
+ }
+ return x;
+}
+
+static void stbte__draw_frame_delayed(int x0, int y0, int x1, int y1, int color)
+{
+ if (stbte__ui.delaycount < STBTE__MAX_DELAYRECT) {
+ stbte__colorrect r = { 0 };
+ stbte__ui.delayrect[stbte__ui.delaycount++] = r;
+ }
+}
+
+static void stbte__flush_delay(void)
+{
+ stbte__colorrect *r;
+ int i;
+ r = stbte__ui.delayrect;
+ for (i=0; i < stbte__ui.delaycount; ++i,++r)
+ stbte__draw_frame(r->x0,r->y0,r->x1,r->y1,r->color);
+ stbte__ui.delaycount = 0;
+}
+
+static void stbte__activate(int id)
+{
+ stbte__ui.active_id = id;
+ stbte__ui.active_event = stbte__ui.event;
+ stbte__ui.accum_x = 0;
+ stbte__ui.accum_y = 0;
+}
+
+static int stbte__hittest(int x0, int y0, int x1, int y1, int id)
+{
+ int over = stbte__ui.mx >= x0 && stbte__ui.my >= y0
+ && stbte__ui.mx < x1 && stbte__ui.my < y1;
+
+ if (over && stbte__ui.event >= STBTE__tick)
+ stbte__ui.next_hot_id = id;
+
+ return over;
+}
+
+static int stbte__button_core(int id)
+{
+ switch (stbte__ui.event) {
+ case STBTE__leftdown:
+ if (stbte__ui.hot_id == id && STBTE__INACTIVE())
+ stbte__activate(id);
+ break;
+ case STBTE__leftup:
+ if (stbte__ui.active_id == id && STBTE__IS_HOT(id)) {
+ stbte__activate(0);
+ return 1;
+ }
+ break;
+ case STBTE__rightdown:
+ if (stbte__ui.hot_id == id && STBTE__INACTIVE())
+ stbte__activate(id);
+ break;
+ case STBTE__rightup:
+ if (stbte__ui.active_id == id && STBTE__IS_HOT(id)) {
+ stbte__activate(0);
+ return -1;
+ }
+ break;
+ }
+ return 0;
+}
+
+static void stbte__draw_box(int x0, int y0, int x1, int y1, int colormode, int colorindex)
+{
+ stbte__draw_rect (x0,y0,x1,y1, stbte__color_table[colormode][STBTE__base ][colorindex]);
+ stbte__draw_frame(x0,y0,x1,y1, stbte__color_table[colormode][STBTE__outline][colorindex]);
+}
+
+static void stbte__draw_textbox(int x0, int y0, int x1, int y1, char *text, int xoff, int yoff, int colormode, int colorindex)
+{
+ stbte__draw_box(x0,y0,x1,y1,colormode,colorindex);
+ stbte__draw_text(x0+xoff,y0+yoff, text, x1-x0-xoff-1, stbte__color_table[colormode][STBTE__text][colorindex]);
+}
+
+static int stbte__button(int colormode, const char *label, int x, int y, int textoff, int width, int id, int toggled, int disabled)
+{
+ int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT;
+ int s = STBTE__BUTTON_INTERNAL_SPACING;
+
+ if(!disabled) stbte__hittest(x0,y0,x1,y1,id);
+
+ if (stbte__ui.event == STBTE__paint)
+ stbte__draw_textbox(x0,y0,x1,y1, (char*) label,s+textoff,s, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled));
+ if (disabled)
+ return 0;
+ return (stbte__button_core(id) == 1);
+}
+
+static int stbte__button_icon(int colormode, char ch, int x, int y, int width, int id, int toggled, int disabled)
+{
+ int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT;
+ int s = STBTE__BUTTON_INTERNAL_SPACING;
+
+ stbte__hittest(x0,y0,x1,y1,id);
+
+ if (stbte__ui.event == STBTE__paint) {
+ char label[2] = { 0 };
+ int pad = (9 - stbte__get_char_width(ch))/2;
+ stbte__draw_textbox(x0,y0,x1,y1, label,s+pad,s, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled));
+ }
+ if (disabled)
+ return 0;
+ return (stbte__button_core(id) == 1);
+}
+
+static int stbte__minibutton(int colormode, int x, int y, int ch, int id)
+{
+ int x0 = x, y0 = y, x1 = x+8, y1 = y+7;
+ stbte__hittest(x0,y0,x1,y1,id);
+ if (stbte__ui.event == STBTE__paint) {
+ char str[2] = {0 };
+ stbte__draw_textbox(x0,y0,x1,y1, str,1,0,colormode, STBTE__INDEX_FOR_ID(id,0,0));
+ }
+ return stbte__button_core(id);
+}
+
+static int stbte__layerbutton(int x, int y, int ch, int id, int toggled, int disabled, int colormode)
+{
+ int x0 = x, y0 = y, x1 = x+10, y1 = y+11;
+ if(!disabled) stbte__hittest(x0,y0,x1,y1,id);
+ if (stbte__ui.event == STBTE__paint) {
+ char str[2] = { 0 };
+ int off = (9-stbte__get_char_width(ch))/2;
+ stbte__draw_textbox(x0,y0,x1,y1, str, off+1,2, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled));
+ }
+ if (disabled)
+ return 0;
+ return stbte__button_core(id);
+}
+
+static int stbte__microbutton(int x, int y, int size, int id, int colormode)
+{
+ int x0 = x, y0 = y, x1 = x+size, y1 = y+size;
+ stbte__hittest(x0,y0,x1,y1,id);
+ if (stbte__ui.event == STBTE__paint) {
+ stbte__draw_box(x0,y0,x1,y1, colormode, STBTE__INDEX_FOR_ID(id,0,0));
+ }
+ return stbte__button_core(id);
+}
+
+static int stbte__microbutton_dragger(int x, int y, int size, int id, int *pos)
+{
+ int x0 = x, y0 = y, x1 = x+size, y1 = y+size;
+ stbte__hittest(x0,y0,x1,y1,id);
+ switch (stbte__ui.event) {
+ case STBTE__paint:
+ stbte__draw_box(x0,y0,x1,y1, STBTE__cexpander, STBTE__INDEX_FOR_ID(id,0,0));
+ break;
+ case STBTE__leftdown:
+ if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
+ stbte__activate(id);
+ stbte__ui.sx = stbte__ui.mx - *pos;
+ }
+ break;
+ case STBTE__mousemove:
+ if (STBTE__IS_ACTIVE(id) && stbte__ui.active_event == STBTE__leftdown) {
+ *pos = stbte__ui.mx - stbte__ui.sx;
+ }
+ break;
+ case STBTE__leftup:
+ if (STBTE__IS_ACTIVE(id))
+ stbte__activate(0);
+ break;
+ default:
+ return stbte__button_core(id);
+ }
+ return 0;
+}
+
+static int stbte__category_button(const char *label, int x, int y, int width, int id, int toggled)
+{
+ int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT;
+ int s = STBTE__BUTTON_INTERNAL_SPACING;
+
+ stbte__hittest(x0,y0,x1,y1,id);
+
+ if (stbte__ui.event == STBTE__paint)
+ stbte__draw_textbox(x0,y0,x1,y1, (char*) label, s,s, STBTE__ccategory_button, STBTE__INDEX_FOR_ID(id,0,toggled));
+
+ return (stbte__button_core(id) == 1);
+}
+
+enum
+{
+ STBTE__none,
+ STBTE__begin,
+ STBTE__end,
+ STBTE__change,
+};
+
+
+static int stbte__slider(int x0, int w, int y, int range, int *value, int id)
+{
+ int x1 = x0+w;
+ int pos = *value * w / (range+1);
+ stbte__hittest(x0,y-2,x1,y+3,id);
+ int event_mouse_move = STBTE__change;
+ switch (stbte__ui.event) {
+ case STBTE__paint:
+ stbte__draw_rect(x0,y,x1,y+1, 0x808080);
+ stbte__draw_rect(x0+pos-1,y-1,x0+pos+2,y+2, 0xffffff);
+ break;
+ case STBTE__leftdown:
+ if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
+ stbte__activate(id);
+ event_mouse_move = STBTE__begin;
+ }
+
+ case STBTE__mousemove:
+ if (STBTE__IS_ACTIVE(id)) {
+ int v = (stbte__ui.mx-x0)*(range+1)/w;
+ if (v < 0) v = 0; else if (v > range) v = range;
+ *value = v;
+ return event_mouse_move;
+ }
+ break;
+ case STBTE__leftup:
+ if (STBTE__IS_ACTIVE(id)) {
+ stbte__activate(0);
+ return STBTE__end;
+ }
+ break;
+ }
+ return STBTE__none;
+}
+
+#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__)
+ #define stbte__sprintf sprintf_s
+ #define stbte__sizeof(s) , sizeof(s)
+#else
+ #define stbte__sprintf sprintf
+ #define stbte__sizeof(s)
+#endif
+
+static int stbte__float_control(int x0, int y0, int w, float minv, float maxv, float scale, const char *fmt, float *value, int colormode, int id)
+{
+ int x1 = x0+w;
+ int y1 = y0+11;
+ stbte__hittest(x0,y0,x1,y1,id);
+ switch (stbte__ui.event) {
+ case STBTE__paint: {
+ char text[32];
+ stbte__sprintf(text stbte__sizeof(text), fmt ? fmt : "%6.2f", *value);
+ stbte__draw_textbox(x0,y0,x1,y1, text, 1,2, colormode, STBTE__INDEX_FOR_ID(id,0,0));
+ break;
+ }
+ case STBTE__leftdown:
+ case STBTE__rightdown:
+ if (STBTE__IS_HOT(id) && STBTE__INACTIVE())
+ stbte__activate(id);
+ return STBTE__begin;
+ break;
+ case STBTE__leftup:
+ case STBTE__rightup:
+ if (STBTE__IS_ACTIVE(id)) {
+ stbte__activate(0);
+ return STBTE__end;
+ }
+ break;
+ case STBTE__mousemove:
+ if (STBTE__IS_ACTIVE(id)) {
+ float v = *value, delta;
+ int ax = stbte__ui.accum_x/STBTE_FLOAT_CONTROL_GRANULARITY;
+ int ay = stbte__ui.accum_y/STBTE_FLOAT_CONTROL_GRANULARITY;
+ stbte__ui.accum_x -= ax*STBTE_FLOAT_CONTROL_GRANULARITY;
+ stbte__ui.accum_y -= ay*STBTE_FLOAT_CONTROL_GRANULARITY;
+ if (stbte__ui.shift) {
+ if (stbte__ui.active_event == STBTE__leftdown)
+ delta = ax * 16.0f + ay;
+ else
+ delta = ax / 16.0f + ay / 256.0f;
+ } else {
+ if (stbte__ui.active_event == STBTE__leftdown)
+ delta = ax*10.0f + ay;
+ else
+ delta = ax * 0.1f + ay * 0.01f;
+ }
+ v += delta * scale;
+ if (v < minv) v = minv;
+ if (v > maxv) v = maxv;
+ *value = v;
+ return STBTE__change;
+ }
+ break;
+ }
+ return STBTE__none;
+}
+
+static void stbte__scrollbar(int x, int y0, int y1, int *val, int v0, int v1, int num_vis, int id)
+{
+ int thumbpos;
+ if (v1 - v0 <= num_vis)
+ return;
+
+
+ thumbpos = y0+2 + (y1-y0-4) * *val / (v1 - v0 - num_vis);
+ if (thumbpos < y0) thumbpos = y0;
+ if (thumbpos >= y1) thumbpos = y1;
+ stbte__hittest(x-1,y0,x+2,y1,id);
+ switch (stbte__ui.event) {
+ case STBTE__paint:
+ stbte__draw_rect(x,y0,x+1,y1, stbte__color_table[STBTE__cscrollbar][STBTE__text][STBTE__idle]);
+ stbte__draw_box(x-1,thumbpos-3,x+2,thumbpos+4, STBTE__cscrollbar, STBTE__INDEX_FOR_ID(id,0,0));
+ break;
+ case STBTE__leftdown:
+ if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
+
+ stbte__activate(id);
+ *val = ((stbte__ui.my-y0) * (v1 - v0 - num_vis) + (y1-y0)/2)/ (y1-y0);
+ }
+ break;
+ case STBTE__mousemove:
+ if (STBTE__IS_ACTIVE(id) && stbte__ui.mx >= x-15 && stbte__ui.mx <= x+15)
+ *val = ((stbte__ui.my-y0) * (v1 - v0 - num_vis) + (y1-y0)/2)/ (y1-y0);
+ break;
+ case STBTE__leftup:
+ if (STBTE__IS_ACTIVE(id))
+ stbte__activate(0);
+ break;
+
+ }
+
+ if (*val >= v1-num_vis)
+ *val = v1-num_vis;
+ if (*val <= v0)
+ *val = v0;
+}
+
+
+static void stbte__compute_digits(stbte_tilemap *tm)
+{
+ if (tm->max_x >= 1000 || tm->max_y >= 1000)
+ tm->digits = 4;
+ else if (tm->max_x >= 100 || tm->max_y >= 100)
+ tm->digits = 3;
+ else
+ tm->digits = 2;
+}
+
+static int stbte__is_single_selection(void)
+{
+ return stbte__ui.has_selection
+ && stbte__ui.select_x0 == stbte__ui.select_x1
+ && stbte__ui.select_y0 == stbte__ui.select_y1;
+}
+
+typedef struct
+{
+ int width, height;
+ int x,y;
+ int active;
+ float retracted;
+} stbte__region_t;
+
+static stbte__region_t stbte__region[4];
+
+#define STBTE__TOOLBAR_ICON_SIZE (9+2*2)
+#define STBTE__TOOLBAR_PASTE_SIZE (34+2*2)
+
+
+
+
+static void stbte__compute_panel_locations(stbte_tilemap *tm)
+{
+ int i, limit, w, k;
+ int window_width = stbte__ui.x1 - stbte__ui.x0;
+ int window_height = stbte__ui.y1 - stbte__ui.y0;
+ int min_width[STBTE__num_panel]={0,0,0,0,0,0,0};
+ int height[STBTE__num_panel]={0,0,0,0,0,0,0};
+ int panel_active[STBTE__num_panel]={1,0,1,1,1,1,1};
+ int vpos[4] = { 0,0,0,0 };
+ stbte__panel *p = stbte__ui.panel;
+ stbte__panel *pt = &p[STBTE__panel_toolbar];
+#ifdef STBTE__NO_PROPS
+ int props = 0;
+#else
+ int props = 1;
+#endif
+
+ for (i=0; i < 4; ++i) {
+ stbte__region[i].active = 0;
+ stbte__region[i].width = 0;
+ stbte__region[i].height = 0;
+ }
+
+
+ stbte__compute_digits(tm);
+
+
+ panel_active[STBTE__panel_categories] = tm->num_categories != 0;
+ panel_active[STBTE__panel_layers ] = tm->num_layers > 1;
+#ifdef STBTE__COLORPICKER
+ panel_active[STBTE__panel_colorpick ] = 1;
+#endif
+
+ panel_active[STBTE__panel_props ] = props && stbte__is_single_selection();
+
+
+ min_width[STBTE__panel_info ] = 8 + 11 + 7*tm->digits+17+7;
+ min_width[STBTE__panel_colorpick ] = 120;
+ min_width[STBTE__panel_tiles ] = 4 + tm->palette_spacing_x + 5;
+ min_width[STBTE__panel_categories] = 4 + 42 + 5;
+ min_width[STBTE__panel_layers ] = 4 + 54 + 30*tm->has_layer_names;
+ min_width[STBTE__panel_toolbar ] = 4 + STBTE__TOOLBAR_PASTE_SIZE;
+ min_width[STBTE__panel_props ] = 80;
+
+
+ stbte__region[0].width = stbte__ui.left_width;
+ stbte__region[1].width = stbte__ui.right_width;
+
+ for (i=0; i < STBTE__num_panel; ++i) {
+ if (panel_active[i]) {
+ int side = stbte__ui.panel[i].side;
+ if (min_width[i] > stbte__region[side].width)
+ stbte__region[side].width = min_width[i];
+ stbte__region[side].active = 1;
+ }
+ }
+
+
+
+
+ if (stbte__region[STBTE__side_top].active) {
+ int height = STBTE__TOOLBAR_ICON_SIZE+2;
+ pt->x0 = stbte__ui.x0;
+ pt->y0 = stbte__ui.y0;
+ pt->width = window_width;
+ pt->height = height;
+ vpos[STBTE__side_left] = vpos[STBTE__side_right] = height;
+ } else {
+ int num_rows = STBTE__num_tool * ((stbte__region[pt->side].width-4)/STBTE__TOOLBAR_ICON_SIZE);
+ height[STBTE__panel_toolbar] = num_rows*13 + 3*15 + 4;
+ }
+
+ for (i=0; i < 4; ++i)
+ stbte__region[i].y = stbte__ui.y0 + vpos[i];
+
+ for (i=0; i < 2; ++i) {
+ int anim = (int) (stbte__region[i].width * stbte__region[i].retracted);
+ stbte__region[i].x = (i == STBTE__side_left) ? stbte__ui.x0 - anim : stbte__ui.x1 - stbte__region[i].width + anim;
+ }
+
+
+ height[STBTE__panel_colorpick] = 300;
+
+
+ w = stbte__region[p[STBTE__panel_info].side].width;
+ p[STBTE__panel_info].mode = (w >= 8 + (11+7*tm->digits+17)*2 + 4);
+ if (p[STBTE__panel_info].mode)
+ height[STBTE__panel_info] = 5 + 11*2 + 2 + tm->palette_spacing_y;
+ else
+ height[STBTE__panel_info] = 5 + 11*4 + 2 + tm->palette_spacing_y;
+
+
+ limit = 6 + stbte__ui.panel[STBTE__panel_layers].delta_height;
+ height[STBTE__panel_layers] = (tm->num_layers > limit ? limit : tm->num_layers)*15 + 7 + (tm->has_layer_names ? 0 : 11) + props*13;
+
+
+ limit = 6 + stbte__ui.panel[STBTE__panel_categories].delta_height;
+ height[STBTE__panel_categories] = (tm->num_categories+1 > limit ? limit : tm->num_categories+1)*11 + 14;
+ if (stbte__ui.panel[STBTE__panel_categories].side == stbte__ui.panel[STBTE__panel_categories].side)
+ height[STBTE__panel_categories] -= 4;
+
+
+ k = (stbte__region[p[STBTE__panel_tiles].side].width - 8) / tm->palette_spacing_x;
+ if (k == 0) k = 1;
+ height[STBTE__panel_tiles] = ((tm->num_tiles+k-1)/k) * tm->palette_spacing_y + 8;
+
+
+ height[STBTE__panel_props] = 9 + STBTE_MAX_PROPERTIES*14;
+
+
+ for (i=0; i < STBTE__num_panel; ++i) {
+ if (panel_active[i]) {
+ int side = p[i].side;
+ if (side == STBTE__side_left || side == STBTE__side_right) {
+ p[i].width = stbte__region[side].width;
+ p[i].x0 = stbte__region[side].x;
+ p[i].y0 = stbte__ui.y0 + vpos[side];
+ p[i].height = height[i];
+ vpos[side] += height[i];
+ if (vpos[side] > window_height) {
+ vpos[side] = window_height;
+ p[i].height = stbte__ui.y1 - p[i].y0;
+ }
+ } else {
+ ;
+ }
+ } else {
+
+ p[i].height = 0;
+ p[i].width = 0;
+ p[i].x0 = stbte__ui.x1;
+ p[i].y0 = stbte__ui.y1;
+ }
+ }
+}
+
+
+enum
+{
+ STBTE__map=1,
+ STBTE__region,
+ STBTE__panel,
+ STBTE__info,
+ STBTE__toolbarA, STBTE__toolbarB,
+ STBTE__palette,
+ STBTE__categories,
+ STBTE__layer,
+ STBTE__solo, STBTE__hide, STBTE__lock,
+ STBTE__scrollbar,
+ STBTE__panel_mover,
+ STBTE__panel_sizer,
+ STBTE__scrollbar_id,
+ STBTE__colorpick_id,
+ STBTE__prop_flag,
+ STBTE__prop_float,
+ STBTE__prop_int,
+};
+
+
+
+
+#define STBTE__ID(n,p) ((n) + ((p)<<7))
+#define STBTE__ID2(n,p,q) STBTE__ID(n, ((p)<<12)+(q) )
+#define STBTE__IDMAP(x,y) STBTE__ID2(STBTE__map, x,y)
+
+static void stbte__activate_map(int x, int y)
+{
+ stbte__ui.active_id = STBTE__IDMAP(x,y);
+ stbte__ui.active_event = stbte__ui.event;
+ stbte__ui.sx = x;
+ stbte__ui.sy = y;
+}
+
+static void stbte__alert(const char *msg)
+{
+ stbte__ui.alert_msg = msg;
+ stbte__ui.alert_timer = 3;
+}
+
+#define STBTE__BG(tm,layer) ((layer) == 0 ? (tm)->background_tile : STBTE__NO_TILE)
+
+
+
+static void stbte__brush_predict(stbte_tilemap *tm, short result[])
+{
+ stbte__tileinfo *ti;
+ int i;
+
+ if (tm->cur_tile < 0) return;
+
+ ti = &tm->tiles[tm->cur_tile];
+
+
+ for (i=0; i < tm->num_layers; ++i) {
+
+ if (!(ti->layermask & (1 << i)))
+ continue;
+
+ if (i != tm->solo_layer) {
+
+ if (tm->cur_layer >= 0 && i != tm->cur_layer)
+ continue;
+
+
+ if (tm->layerinfo[i].hidden)
+ continue;
+
+
+ if (tm->layerinfo[i].locked == STBTE__locked)
+ continue;
+
+
+ if (tm->layerinfo[i].locked == STBTE__protected && result[i] != STBTE__BG(tm,i))
+ continue;
+ }
+
+ result[i] = ti->id;
+ return;
+ }
+}
+
+static void stbte__brush(stbte_tilemap *tm, int x, int y)
+{
+ stbte__tileinfo *ti;
+
+
+ int i;
+
+ if (tm->cur_tile < 0) return;
+
+ ti = &tm->tiles[tm->cur_tile];
+
+ for (i=0; i < tm->num_layers; ++i) {
+
+ if (!(ti->layermask & (1 << i)))
+ continue;
+
+ if (i != tm->solo_layer) {
+
+ if (tm->cur_layer >= 0 && i != tm->cur_layer)
+ continue;
+
+
+ if (tm->layerinfo[i].hidden)
+ continue;
+
+
+ if (tm->layerinfo[i].locked == STBTE__locked)
+ continue;
+
+
+ if (tm->layerinfo[i].locked == STBTE__protected && tm->data[y][x][i] != STBTE__BG(tm,i))
+ continue;
+ }
+
+ stbte__undo_record(tm,x,y,i,tm->data[y][x][i]);
+ tm->data[y][x][i] = ti->id;
+ return;
+ }
+
+
+}
+
+enum
+{
+ STBTE__erase_none = -1,
+ STBTE__erase_brushonly = 0,
+ STBTE__erase_any = 1,
+ STBTE__erase_all = 2,
+};
+
+static int stbte__erase_predict(stbte_tilemap *tm, short result[], int allow_any)
+{
+ stbte__tileinfo *ti = tm->cur_tile >= 0 ? &tm->tiles[tm->cur_tile] : NULL;
+ int i;
+
+ if (allow_any == STBTE__erase_none)
+ return allow_any;
+
+
+ i = tm->cur_layer;
+ if (tm->solo_layer >= 0)
+ i = tm->solo_layer;
+
+
+ if (i >= 0) {
+ short bg = (i == 0 ? tm->background_tile : -1);
+ if (tm->solo_layer < 0) {
+
+ if (tm->layerinfo[i].hidden) return STBTE__erase_none;
+ if (tm->layerinfo[i].locked) return STBTE__erase_none;
+ }
+ if (result[i] == bg)
+ return STBTE__erase_none;
+ if (ti && result[i] == ti->id && (i != 0 || ti->id != tm->background_tile)) {
+ result[i] = bg;
+ return STBTE__erase_brushonly;
+ }
+ if (allow_any == STBTE__erase_any) {
+ result[i] = bg;
+ return STBTE__erase_any;
+ }
+ return STBTE__erase_none;
+ }
+
+
+
+ if (ti && allow_any != STBTE__erase_all) {
+ for (i=tm->num_layers-1; i >= 0; --i) {
+ if (result[i] != ti->id)
+ continue;
+ if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden)
+ continue;
+ if (i == 0 && result[i] == tm->background_tile)
+ return STBTE__erase_none;
+ result[i] = STBTE__BG(tm,i);
+ return STBTE__erase_brushonly;
+ }
+ }
+
+ if (allow_any != STBTE__erase_any && allow_any != STBTE__erase_all)
+ return STBTE__erase_none;
+
+
+ for (i=tm->num_layers-1; i >= 0; --i) {
+ if (result[i] < 0)
+ continue;
+ if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden)
+ continue;
+ if (i == 0 && result[i] == tm->background_tile)
+ return STBTE__erase_none;
+ result[i] = STBTE__BG(tm,i);
+ if (allow_any != STBTE__erase_all)
+ return STBTE__erase_any;
+ }
+
+ if (allow_any == STBTE__erase_all)
+ return allow_any;
+ return STBTE__erase_none;
+}
+
+static int stbte__erase(stbte_tilemap *tm, int x, int y, int allow_any)
+{
+ stbte__tileinfo *ti = tm->cur_tile >= 0 ? &tm->tiles[tm->cur_tile] : NULL;
+ int i;
+
+ if (allow_any == STBTE__erase_none)
+ return allow_any;
+
+
+ i = tm->cur_layer;
+ if (tm->solo_layer >= 0)
+ i = tm->solo_layer;
+
+
+ if (i >= 0) {
+ short bg = (i == 0 ? tm->background_tile : -1);
+ if (tm->solo_layer < 0) {
+
+ if (tm->layerinfo[i].hidden) return STBTE__erase_none;
+ if (tm->layerinfo[i].locked) return STBTE__erase_none;
+ }
+ if (tm->data[y][x][i] == bg)
+ return -1;
+ if (ti && tm->data[y][x][i] == ti->id && (i != 0 || ti->id != tm->background_tile)) {
+ stbte__undo_record(tm,x,y,i,tm->data[y][x][i]);
+ tm->data[y][x][i] = bg;
+ return STBTE__erase_brushonly;
+ }
+ if (allow_any == STBTE__erase_any) {
+ stbte__undo_record(tm,x,y,i,tm->data[y][x][i]);
+ tm->data[y][x][i] = bg;
+ return STBTE__erase_any;
+ }
+ return STBTE__erase_none;
+ }
+
+
+
+ if (ti && allow_any != STBTE__erase_all) {
+ for (i=tm->num_layers-1; i >= 0; --i) {
+ if (tm->data[y][x][i] != ti->id)
+ continue;
+ if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden)
+ continue;
+ if (i == 0 && tm->data[y][x][i] == tm->background_tile)
+ return STBTE__erase_none;
+ stbte__undo_record(tm,x,y,i,tm->data[y][x][i]);
+ tm->data[y][x][i] = STBTE__BG(tm,i);
+ return STBTE__erase_brushonly;
+ }
+ }
+
+ if (allow_any != STBTE__erase_any && allow_any != STBTE__erase_all)
+ return STBTE__erase_none;
+
+
+ for (i=tm->num_layers-1; i >= 0; --i) {
+ if (tm->data[y][x][i] < 0)
+ continue;
+ if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden)
+ continue;
+ if (i == 0 && tm->data[y][x][i] == tm->background_tile)
+ return STBTE__erase_none;
+ stbte__undo_record(tm,x,y,i,tm->data[y][x][i]);
+ tm->data[y][x][i] = STBTE__BG(tm,i);
+ if (allow_any != STBTE__erase_all)
+ return STBTE__erase_any;
+ }
+ if (allow_any == STBTE__erase_all)
+ return allow_any;
+ return STBTE__erase_none;
+}
+
+static int stbte__find_tile(stbte_tilemap *tm, int tile_id)
+{
+ int i;
+ for (i=0; i < tm->num_tiles; ++i)
+ if (tm->tiles[i].id == tile_id)
+ return i;
+ stbte__alert("Eyedropped tile that isn't in tileset");
+ return -1;
+}
+
+static void stbte__eyedrop(stbte_tilemap *tm, int x, int y)
+{
+ int i,j;
+
+
+ if (stbte__ui.eyedrop_x != x || stbte__ui.eyedrop_y != y) {
+ stbte__ui.eyedrop_x = x;
+ stbte__ui.eyedrop_y = y;
+ stbte__ui.eyedrop_last_layer = tm->num_layers;
+ }
+
+
+ i = tm->cur_layer;
+ if (tm->solo_layer >= 0)
+ i = tm->solo_layer;
+ if (i >= 0) {
+ if (tm->data[y][x][i] == STBTE__NO_TILE)
+ return;
+ tm->cur_tile = stbte__find_tile(tm, tm->data[y][x][i]);
+ return;
+ }
+
+
+ i = stbte__ui.eyedrop_last_layer;
+ for (j=0; j < tm->num_layers; ++j) {
+ if (--i < 0)
+ i = tm->num_layers-1;
+ if (tm->layerinfo[i].hidden)
+ continue;
+ if (tm->data[y][x][i] == STBTE__NO_TILE)
+ continue;
+ stbte__ui.eyedrop_last_layer = i;
+ tm->cur_tile = stbte__find_tile(tm, tm->data[y][x][i]);
+ return;
+ }
+}
+
+static int stbte__should_copy_properties(stbte_tilemap *tm)
+{
+ int i;
+ if (tm->propmode == STBTE__propmode_always)
+ return 1;
+ if (tm->propmode == STBTE__propmode_never)
+ return 0;
+ if (tm->solo_layer >= 0 || tm->cur_layer >= 0)
+ return 0;
+ for (i=0; i < tm->num_layers; ++i)
+ if (tm->layerinfo[i].hidden || tm->layerinfo[i].locked)
+ return 0;
+ return 1;
+}
+
+
+static void stbte__paste_stack(stbte_tilemap *tm, short result[], short dest[], short src[], int dragging)
+{
+ int i;
+
+
+ i = tm->cur_layer;
+ if (tm->solo_layer >= 0)
+ i = tm->solo_layer;
+ if (i >= 0) {
+ if (tm->solo_layer < 0) {
+
+ if (tm->layerinfo[i].hidden) return;
+ if (tm->layerinfo[i].locked == STBTE__locked) return;
+
+ if (tm->layerinfo[i].locked == STBTE__protected && dest[i] != STBTE__BG(tm,i)) return;
+
+ if (dragging && tm->layerinfo[i].locked == STBTE__protected)
+ return;
+ }
+ result[i] = dest[i];
+ if (src[i] != STBTE__BG(tm,i))
+ result[i] = src[i];
+ return;
+ }
+
+ for (i=0; i < tm->num_layers; ++i) {
+ result[i] = dest[i];
+ if (src[i] != STBTE__NO_TILE)
+ if (!tm->layerinfo[i].hidden && tm->layerinfo[i].locked != STBTE__locked)
+ if (tm->layerinfo[i].locked == STBTE__unlocked || (!dragging && dest[i] == STBTE__BG(tm,i)))
+ result[i] = src[i];
+ }
+}
+
+
+static void stbte__clear_stack(stbte_tilemap *tm, short result[])
+{
+ int i;
+
+ i = tm->cur_layer;
+ if (tm->solo_layer >= 0)
+ i = tm->solo_layer;
+ if (i >= 0)
+ result[i] = STBTE__BG(tm,i);
+ else
+ for (i=0; i < tm->num_layers; ++i)
+ if (!tm->layerinfo[i].hidden && tm->layerinfo[i].locked == STBTE__unlocked)
+ result[i] = STBTE__BG(tm,i);
+}
+
+
+#define STBTE__IS_MAP_ACTIVE() ((stbte__ui.active_id & 127) == STBTE__map)
+#define STBTE__IS_MAP_HOT() ((stbte__ui.hot_id & 127) == STBTE__map)
+
+static void stbte__fillrect(stbte_tilemap *tm, int x0, int y0, int x1, int y1, int fill)
+{
+ int i,j;
+
+ stbte__begin_undo(tm);
+ if (x0 > x1) i=x0,x0=x1,x1=i;
+ if (y0 > y1) j=y0,y0=y1,y1=j;
+ for (j=y0; j <= y1; ++j)
+ for (i=x0; i <= x1; ++i)
+ if (fill)
+ stbte__brush(tm, i,j);
+ else
+ stbte__erase(tm, i,j,STBTE__erase_any);
+ stbte__end_undo(tm);
+
+ stbte__ui.alert_msg = 0;
+}
+
+static void stbte__select_rect(stbte_tilemap *tm, int x0, int y0, int x1, int y1)
+{
+ stbte__ui.has_selection = 1;
+ stbte__ui.select_x0 = (x0 < x1 ? x0 : x1);
+ stbte__ui.select_x1 = (x0 < x1 ? x1 : x0);
+ stbte__ui.select_y0 = (y0 < y1 ? y0 : y1);
+ stbte__ui.select_y1 = (y0 < y1 ? y1 : y0);
+}
+
+static void stbte__copy_properties(float *dest, float *src)
+{
+ int i;
+ for (i=0; i < STBTE_MAX_PROPERTIES; ++i)
+ dest[i] = src[i];
+}
+
+static void stbte__copy_cut(stbte_tilemap *tm, int cut)
+{
+ int i,j,n,w,h,p=0;
+ int copy_props = stbte__should_copy_properties(tm);
+ if (!stbte__ui.has_selection)
+ return;
+ w = stbte__ui.select_x1 - stbte__ui.select_x0 + 1;
+ h = stbte__ui.select_y1 - stbte__ui.select_y0 + 1;
+ if (STBTE_MAX_COPY / w < h) {
+ stbte__alert("Selection too large for copy buffer, increase STBTE_MAX_COPY");
+ return;
+ }
+
+ for (i=0; i < w*h; ++i)
+ for (n=0; n < tm->num_layers; ++n)
+ stbte__ui.copybuffer[i][n] = STBTE__NO_TILE;
+
+ if (cut)
+ stbte__begin_undo(tm);
+ for (j=stbte__ui.select_y0; j <= stbte__ui.select_y1; ++j) {
+ for (i=stbte__ui.select_x0; i <= stbte__ui.select_x1; ++i) {
+ for (n=0; n < tm->num_layers; ++n) {
+ if (tm->solo_layer >= 0) {
+ if (tm->solo_layer != n)
+ continue;
+ } else {
+ if (tm->cur_layer >= 0)
+ if (tm->cur_layer != n)
+ continue;
+ if (tm->layerinfo[n].hidden)
+ continue;
+ if (cut && tm->layerinfo[n].locked)
+ continue;
+ }
+ stbte__ui.copybuffer[p][n] = tm->data[j][i][n];
+ if (cut) {
+ stbte__undo_record(tm,i,j,n, tm->data[j][i][n]);
+ tm->data[j][i][n] = (n==0 ? tm->background_tile : -1);
+ }
+ }
+ if (copy_props) {
+ stbte__copy_properties(stbte__ui.copyprops[p], tm->props[j][i]);
+#ifdef STBTE_ALLOW_LINK
+ stbte__ui.copylinks[p] = tm->link[j][i];
+ if (cut)
+ stbte__set_link(tm, i,j,-1,-1, STBTE__undo_record);
+#endif
+ }
+ ++p;
+ }
+ }
+ if (cut)
+ stbte__end_undo(tm);
+ stbte__ui.copy_width = w;
+ stbte__ui.copy_height = h;
+ stbte__ui.has_copy = 1;
+
+ stbte__ui.copy_has_props = copy_props;
+ stbte__ui.copy_src = tm;
+ stbte__ui.copy_src_x = stbte__ui.select_x0;
+ stbte__ui.copy_src_y = stbte__ui.select_y0;
+}
+
+static int stbte__in_rect(int x, int y, int x0, int y0, int w, int h)
+{
+ return x >= x0 && x < x0+w && y >= y0 && y < y0+h;
+}
+
+#ifdef STBTE_ALLOW_LINK
+static int stbte__in_src_rect(int x, int y)
+{
+ return stbte__in_rect(x,y, stbte__ui.copy_src_x, stbte__ui.copy_src_y, stbte__ui.copy_width, stbte__ui.copy_height);
+}
+
+static int stbte__in_dest_rect(int x, int y, int destx, int desty)
+{
+ return stbte__in_rect(x,y, destx, desty, stbte__ui.copy_width, stbte__ui.copy_height);
+}
+#endif
+
+static void stbte__paste(stbte_tilemap *tm, int mapx, int mapy)
+{
+ int w = stbte__ui.copy_width;
+ int h = stbte__ui.copy_height;
+ int i,j,k,p;
+ int x = mapx - (w>>1);
+ int y = mapy - (h>>1);
+ int copy_props = stbte__should_copy_properties(tm) && stbte__ui.copy_has_props;
+ if (stbte__ui.has_copy == 0)
+ return;
+ stbte__begin_undo(tm);
+ p = 0;
+ for (j=0; j < h; ++j) {
+ for (i=0; i < w; ++i) {
+ if (y+j >= 0 && y+j < tm->max_y && x+i >= 0 && x+i < tm->max_x) {
+
+ short tilestack[STBTE_MAX_LAYERS];
+ for (k=0; k < tm->num_layers; ++k)
+ tilestack[k] = tm->data[y+j][x+i][k];
+ stbte__paste_stack(tm, tilestack, tilestack, stbte__ui.copybuffer[p], 0);
+
+ for (k=0; k < tm->num_layers; ++k) {
+ if (tilestack[k] != tm->data[y+j][x+i][k]) {
+ stbte__undo_record(tm, x+i,y+j,k, tm->data[y+j][x+i][k]);
+ tm->data[y+j][x+i][k] = tilestack[k];
+ }
+ }
+ }
+ if (copy_props) {
+#ifdef STBTE_ALLOW_LINK
+
+ int destx = -1, desty = -1;
+ stbte__link *link = &stbte__ui.copylinks[p];
+
+
+ if (stbte__in_src_rect(link->x, link->y)) {
+
+ destx = x + (link->x - stbte__ui.copy_src_x);
+ desty = y + (link->y - stbte__ui.copy_src_y);
+ } else if (tm == stbte__ui.copy_src) {
+
+ if (!stbte__in_dest_rect(link->x,link->y,x,y)) {
+ destx = link->x;
+ desty = link->y;
+ }
+ }
+
+ if (destx < 0 || destx >= tm->max_x || desty < 0 || desty >= tm->max_y)
+ destx = -1, desty = -1;
+ stbte__set_link(tm, x+i, y+j, destx, desty, STBTE__undo_record);
+#endif
+ for (k=0; k < STBTE_MAX_PROPERTIES; ++k) {
+ if (tm->props[y+j][x+i][k] != stbte__ui.copyprops[p][k])
+ stbte__undo_record_prop_float(tm, x+i, y+j, k, tm->props[y+j][x+i][k]);
+ }
+ stbte__copy_properties(tm->props[y+j][x+i], stbte__ui.copyprops[p]);
+ }
+ ++p;
+ }
+ }
+ stbte__end_undo(tm);
+}
+
+static void stbte__drag_update(stbte_tilemap *tm, int mapx, int mapy, int copy_props)
+{
+ int w = stbte__ui.drag_w, h = stbte__ui.drag_h;
+ int ox,oy,i,deleted=0,written=0;
+ short temp[STBTE_MAX_LAYERS];
+ short *data = NULL;
+
+ STBTE__NOTUSED(deleted);
+ STBTE__NOTUSED(written);
+
+ if (!stbte__ui.shift) {
+ ox = mapx - stbte__ui.drag_x;
+ oy = mapy - stbte__ui.drag_y;
+ if (ox >= 0 && ox < w && oy >= 0 && oy < h) {
+ deleted=1;
+ for (i=0; i < tm->num_layers; ++i)
+ temp[i] = tm->data[mapy][mapx][i];
+ data = temp;
+ stbte__clear_stack(tm, data);
+ }
+ }
+ ox = mapx - stbte__ui.drag_dest_x;
+ oy = mapy - stbte__ui.drag_dest_y;
+
+ if (ox >= 0 && ox < w && oy >= 0 && oy < h) {
+
+ if (stbte__in_rect(stbte__ui.drag_x+ox, stbte__ui.drag_y+oy, 0, 0, tm->max_x, tm->max_y)) {
+ written = 1;
+ if (data == NULL) {
+ for (i=0; i < tm->num_layers; ++i)
+ temp[i] = tm->data[mapy][mapx][i];
+ data = temp;
+ }
+ stbte__paste_stack(tm, data, data, tm->data[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox], !stbte__ui.shift);
+ if (copy_props) {
+ for (i=0; i < STBTE_MAX_PROPERTIES; ++i) {
+ if (tm->props[mapy][mapx][i] != tm->props[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox][i]) {
+ stbte__undo_record_prop_float(tm, mapx, mapy, i, tm->props[mapy][mapx][i]);
+ tm->props[mapy][mapx][i] = tm->props[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox][i];
+ }
+ }
+ }
+ }
+ }
+ if (data) {
+ for (i=0; i < tm->num_layers; ++i) {
+ if (tm->data[mapy][mapx][i] != data[i]) {
+ stbte__undo_record(tm, mapx, mapy, i, tm->data[mapy][mapx][i]);
+ tm->data[mapy][mapx][i] = data[i];
+ }
+ }
+ }
+ #ifdef STBTE_ALLOW_LINK
+ if (copy_props) {
+ int overwritten=0, moved=0, copied=0;
+
+
+
+ stbte__link *k;
+
+ k = &tm->link[mapy][mapx];
+ if (deleted)
+ k = NULL;
+ if (written)
+ k = &tm->link[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox];
+
+
+ if (k && k->x >= 0) {
+ overwritten = stbte__in_rect(k->x, k->y, stbte__ui.drag_dest_x, stbte__ui.drag_dest_y, w, h);
+ if (!stbte__ui.shift)
+ moved = stbte__in_rect(k->x, k->y, stbte__ui.drag_x , stbte__ui.drag_y , w, h);
+ else
+ copied = stbte__in_rect(k->x, k->y, stbte__ui.drag_x , stbte__ui.drag_y , w, h);
+ }
+
+ if (deleted || written || overwritten || moved || copied) {
+
+ if (k == NULL || k->x < 0)
+ stbte__set_link(tm, mapx, mapy, -1, -1, STBTE__undo_record);
+ else if (moved || (copied && written)) {
+
+
+ int x = k->x + (stbte__ui.drag_dest_x - stbte__ui.drag_x);
+ int y = k->y + (stbte__ui.drag_dest_y - stbte__ui.drag_y);
+ if (!(x >= 0 && y >= 0 && x < tm->max_x && y < tm->max_y))
+ x = -1, y = -1;
+ stbte__set_link(tm, mapx, mapy, x, y, STBTE__undo_record);
+ } else if (overwritten) {
+ stbte__set_link(tm, mapx, mapy, -1, -1, STBTE__undo_record);
+ } else
+ stbte__set_link(tm, mapx, mapy, k->x, k->y, STBTE__undo_record);
+ }
+ }
+ #endif
+}
+
+static void stbte__drag_place(stbte_tilemap *tm, int mapx, int mapy)
+{
+ int i,j;
+ int copy_props = stbte__should_copy_properties(tm);
+ int move_x = (stbte__ui.drag_dest_x - stbte__ui.drag_x);
+ int move_y = (stbte__ui.drag_dest_y - stbte__ui.drag_y);
+ if (move_x == 0 && move_y == 0)
+ return;
+
+ stbte__begin_undo(tm);
+
+
+
+ if (move_y > 0 || (move_y == 0 && move_x > 0)) {
+ for (j=tm->max_y-1; j >= 0; --j)
+ for (i=tm->max_x-1; i >= 0; --i)
+ stbte__drag_update(tm,i,j,copy_props);
+ } else {
+ for (j=0; j < tm->max_y; ++j)
+ for (i=0; i < tm->max_x; ++i)
+ stbte__drag_update(tm,i,j,copy_props);
+ }
+ stbte__end_undo(tm);
+
+ stbte__ui.has_selection = 1;
+ stbte__ui.select_x0 = stbte__ui.drag_dest_x;
+ stbte__ui.select_y0 = stbte__ui.drag_dest_y;
+ stbte__ui.select_x1 = stbte__ui.select_x0 + stbte__ui.drag_w - 1;
+ stbte__ui.select_y1 = stbte__ui.select_y0 + stbte__ui.drag_h - 1;
+}
+
+static void stbte__tile_paint(stbte_tilemap *tm, int sx, int sy, int mapx, int mapy, int layer)
+{
+ int i;
+ int id = STBTE__IDMAP(mapx,mapy);
+ int x0=sx, y0=sy;
+ int x1=sx+tm->spacing_x, y1=sy+tm->spacing_y;
+ stbte__hittest(x0,y0,x1,y1, id);
+ short *data = tm->data[mapy][mapx];
+ short temp[STBTE_MAX_LAYERS];
+
+ if (STBTE__IS_MAP_HOT()) {
+ if (stbte__ui.pasting) {
+ int ox = mapx - stbte__ui.paste_x;
+ int oy = mapy - stbte__ui.paste_y;
+ if (ox >= 0 && ox < stbte__ui.copy_width && oy >= 0 && oy < stbte__ui.copy_height) {
+ stbte__paste_stack(tm, temp, tm->data[mapy][mapx], stbte__ui.copybuffer[oy*stbte__ui.copy_width+ox], 0);
+ data = temp;
+ }
+ } else if (stbte__ui.dragging) {
+ int ox,oy;
+ for (i=0; i < tm->num_layers; ++i)
+ temp[i] = tm->data[mapy][mapx][i];
+ data = temp;
+
+
+ ox = mapx - stbte__ui.drag_x;
+ oy = mapy - stbte__ui.drag_y;
+ if (!stbte__ui.shift && ox >= 0 && ox < stbte__ui.drag_w && oy >= 0 && oy < stbte__ui.drag_h) {
+ stbte__clear_stack(tm, temp);
+ }
+
+ ox = mapx - stbte__ui.drag_dest_x;
+ oy = mapy - stbte__ui.drag_dest_y;
+ if (ox >= 0 && ox < stbte__ui.drag_w && oy >= 0 && oy < stbte__ui.drag_h) {
+ stbte__paste_stack(tm, temp, temp, tm->data[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox], !stbte__ui.shift);
+ }
+ } else if (STBTE__IS_MAP_ACTIVE()) {
+ if (stbte__ui.tool == STBTE__tool_rect) {
+ if ((stbte__ui.ms_time & 511) < 380) {
+ int ex = ((stbte__ui.hot_id >> 19) & 4095);
+ int ey = ((stbte__ui.hot_id >> 7) & 4095);
+ int sx = stbte__ui.sx;
+ int sy = stbte__ui.sy;
+
+ if ( ((mapx >= sx && mapx < ex+1) || (mapx >= ex && mapx < sx+1))
+ && ((mapy >= sy && mapy < ey+1) || (mapy >= ey && mapy < sy+1))) {
+ int i;
+ for (i=0; i < tm->num_layers; ++i)
+ temp[i] = tm->data[mapy][mapx][i];
+ data = temp;
+ if (stbte__ui.active_event == STBTE__leftdown)
+ stbte__brush_predict(tm, temp);
+ else
+ stbte__erase_predict(tm, temp, STBTE__erase_any);
+ }
+ }
+ }
+ }
+ }
+
+ if (STBTE__IS_HOT(id) && STBTE__INACTIVE() && !stbte__ui.pasting) {
+ if (stbte__ui.tool == STBTE__tool_brush) {
+ if ((stbte__ui.ms_time & 511) < 300) {
+ data = temp;
+ for (i=0; i < tm->num_layers; ++i)
+ temp[i] = tm->data[mapy][mapx][i];
+ stbte__brush_predict(tm, temp);
+ }
+ }
+ }
+
+ {
+ i = layer;
+ if (i == tm->solo_layer || (!tm->layerinfo[i].hidden && tm->solo_layer < 0))
+ if (data[i] >= 0)
+ STBTE_DRAW_TILE(sx,sy, (unsigned short) data[i], 0, tm->props[mapy][mapx]);
+ }
+}
+
+static void stbte__tile(stbte_tilemap *tm, int sx, int sy, int mapx, int mapy)
+{
+ int tool = stbte__ui.tool;
+ int x0=sx, y0=sy;
+ int x1=sx+tm->spacing_x, y1=sy+tm->spacing_y;
+ int id = STBTE__IDMAP(mapx,mapy);
+ int over = stbte__hittest(x0,y0,x1,y1, id);
+ switch (stbte__ui.event) {
+ case STBTE__paint: {
+ if (stbte__ui.pasting || stbte__ui.dragging || stbte__ui.scrolling)
+ break;
+ if (stbte__ui.scrollkey && !STBTE__IS_MAP_ACTIVE())
+ break;
+ if (STBTE__IS_HOT(id) && STBTE__IS_MAP_ACTIVE() && (tool == STBTE__tool_rect || tool == STBTE__tool_select)) {
+ int rx0,ry0,rx1,ry1,t;
+
+ rx0 = x0 + tm->spacing_x/2;
+ ry0 = y0 + tm->spacing_y/2;
+ rx1 = rx0 + (stbte__ui.sx - mapx) * tm->spacing_x;
+ ry1 = ry0 + (stbte__ui.sy - mapy) * tm->spacing_y;
+ if (rx0 > rx1) t=rx0,rx0=rx1,rx1=t;
+ if (ry0 > ry1) t=ry0,ry0=ry1,ry1=t;
+ rx0 -= tm->spacing_x/2;
+ ry0 -= tm->spacing_y/2;
+ rx1 += tm->spacing_x/2;
+ ry1 += tm->spacing_y/2;
+ stbte__draw_frame(rx0-1,ry0-1,rx1+1,ry1+1, STBTE_COLOR_TILEMAP_HIGHLIGHT);
+ break;
+ }
+ if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
+ stbte__draw_frame(x0-1,y0-1,x1+1,y1+1, STBTE_COLOR_TILEMAP_HIGHLIGHT);
+ }
+#ifdef STBTE_ALLOW_LINK
+ if (stbte__ui.show_links && tm->link[mapy][mapx].x >= 0) {
+ int tx = tm->link[mapy][mapx].x;
+ int ty = tm->link[mapy][mapx].y;
+ int lx0,ly0,lx1,ly1;
+ if (STBTE_ALLOW_LINK(tm->data[mapy][mapx], tm->props[mapy][mapx],
+ tm->data[ty ][tx ], tm->props[ty ][tx ]))
+ {
+ lx0 = x0 + (tm->spacing_x >> 1) - 1;
+ ly0 = y0 + (tm->spacing_y >> 1) - 1;
+ lx1 = lx0 + (tx - mapx) * tm->spacing_x + 2;
+ ly1 = ly0 + (ty - mapy) * tm->spacing_y + 2;
+ stbte__draw_link(lx0,ly0,lx1,ly1,
+ STBTE_LINK_COLOR(tm->data[mapy][mapx], tm->props[mapy][mapx],
+ tm->data[ty ][tx ], tm->props[ty ][tx]));
+ }
+ }
+#endif
+ break;
+ }
+ }
+
+ if (stbte__ui.pasting) {
+ switch (stbte__ui.event) {
+ case STBTE__leftdown:
+ if (STBTE__IS_HOT(id)) {
+ stbte__ui.pasting = 0;
+ stbte__paste(tm, mapx, mapy);
+ stbte__activate(0);
+ }
+ break;
+ case STBTE__leftup:
+
+ stbte__activate(0);
+ break;
+ case STBTE__rightdown:
+ if (STBTE__IS_HOT(id)) {
+ stbte__activate(0);
+ stbte__ui.pasting = 0;
+ }
+ break;
+ }
+ return;
+ }
+
+ if (stbte__ui.scrolling) {
+ if (stbte__ui.event == STBTE__leftup) {
+ stbte__activate(0);
+ stbte__ui.scrolling = 0;
+ }
+ if (stbte__ui.event == STBTE__mousemove) {
+ tm->scroll_x += (stbte__ui.start_x - stbte__ui.mx);
+ tm->scroll_y += (stbte__ui.start_y - stbte__ui.my);
+ stbte__ui.start_x = stbte__ui.mx;
+ stbte__ui.start_y = stbte__ui.my;
+ }
+ return;
+ }
+
+
+ if (STBTE__IS_HOT(id) && stbte__ui.scrollkey && stbte__ui.event == STBTE__leftdown) {
+ stbte__ui.scrolling = 1;
+ stbte__ui.start_x = stbte__ui.mx;
+ stbte__ui.start_y = stbte__ui.my;
+ return;
+ }
+
+ switch (tool) {
+ case STBTE__tool_brush:
+ switch (stbte__ui.event) {
+ case STBTE__mousemove:
+ if (STBTE__IS_MAP_ACTIVE() && over) {
+
+ if (!STBTE__IS_ACTIVE(id)) {
+ if (stbte__ui.active_event == STBTE__leftdown)
+ stbte__brush(tm, mapx, mapy);
+ else
+ stbte__erase(tm, mapx, mapy, stbte__ui.brush_state);
+ stbte__ui.active_id = id;
+ }
+ }
+ break;
+ case STBTE__leftdown:
+ if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
+ stbte__activate(id);
+ stbte__begin_undo(tm);
+ stbte__brush(tm, mapx, mapy);
+ }
+ break;
+ case STBTE__rightdown:
+ if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
+ stbte__activate(id);
+ stbte__begin_undo(tm);
+ if (stbte__erase(tm, mapx, mapy, STBTE__erase_any) == STBTE__erase_brushonly)
+ stbte__ui.brush_state = STBTE__erase_brushonly;
+ else
+ stbte__ui.brush_state = STBTE__erase_any;
+ }
+ break;
+ case STBTE__leftup:
+ case STBTE__rightup:
+ if (STBTE__IS_MAP_ACTIVE()) {
+ stbte__end_undo(tm);
+ stbte__activate(0);
+ }
+ break;
+ }
+ break;
+
+#ifdef STBTE_ALLOW_LINK
+ case STBTE__tool_link:
+ switch (stbte__ui.event) {
+ case STBTE__leftdown:
+ if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
+ stbte__activate(id);
+ stbte__ui.linking = 1;
+ stbte__ui.sx = mapx;
+ stbte__ui.sy = mapy;
+
+ }
+ break;
+ case STBTE__leftup:
+ if (STBTE__IS_HOT(id) && STBTE__IS_MAP_ACTIVE()) {
+ if ((mapx != stbte__ui.sx || mapy != stbte__ui.sy) &&
+ STBTE_ALLOW_LINK(tm->data[stbte__ui.sy][stbte__ui.sx], tm->props[stbte__ui.sy][stbte__ui.sx],
+ tm->data[mapy][mapx], tm->props[mapy][mapx]))
+ stbte__set_link(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy, STBTE__undo_block);
+ else
+ stbte__set_link(tm, stbte__ui.sx, stbte__ui.sy, -1,-1, STBTE__undo_block);
+ stbte__ui.linking = 0;
+ stbte__activate(0);
+ }
+ break;
+
+ case STBTE__rightdown:
+ if (STBTE__IS_ACTIVE(id)) {
+ stbte__activate(0);
+ stbte__ui.linking = 0;
+ }
+ break;
+ }
+ break;
+#endif
+
+ case STBTE__tool_erase:
+ switch (stbte__ui.event) {
+ case STBTE__mousemove:
+ if (STBTE__IS_MAP_ACTIVE() && over)
+ stbte__erase(tm, mapx, mapy, STBTE__erase_all);
+ break;
+ case STBTE__leftdown:
+ if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
+ stbte__activate(id);
+ stbte__begin_undo(tm);
+ stbte__erase(tm, mapx, mapy, STBTE__erase_all);
+ }
+ break;
+ case STBTE__leftup:
+ if (STBTE__IS_MAP_ACTIVE()) {
+ stbte__end_undo(tm);
+ stbte__activate(0);
+ }
+ break;
+ }
+ break;
+
+ case STBTE__tool_select:
+ if (STBTE__IS_HOT(id)) {
+ switch (stbte__ui.event) {
+ case STBTE__leftdown:
+ if (STBTE__INACTIVE()) {
+
+ if (stbte__ui.has_selection) {
+ if ( mapx >= stbte__ui.select_x0 && mapx <= stbte__ui.select_x1
+ && mapy >= stbte__ui.select_y0 && mapy <= stbte__ui.select_y1)
+ {
+ stbte__ui.dragging = 1;
+ stbte__ui.drag_x = stbte__ui.select_x0;
+ stbte__ui.drag_y = stbte__ui.select_y0;
+ stbte__ui.drag_w = stbte__ui.select_x1 - stbte__ui.select_x0 + 1;
+ stbte__ui.drag_h = stbte__ui.select_y1 - stbte__ui.select_y0 + 1;
+ stbte__ui.drag_offx = mapx - stbte__ui.select_x0;
+ stbte__ui.drag_offy = mapy - stbte__ui.select_y0;
+ }
+ }
+ stbte__ui.has_selection = 0;
+ stbte__activate_map(mapx,mapy);
+ }
+ break;
+ case STBTE__leftup:
+ if (STBTE__IS_MAP_ACTIVE()) {
+ if (stbte__ui.dragging) {
+ stbte__drag_place(tm, mapx,mapy);
+ stbte__ui.dragging = 0;
+ stbte__activate(0);
+ } else {
+ stbte__select_rect(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy);
+ stbte__activate(0);
+ }
+ }
+ break;
+ case STBTE__rightdown:
+ stbte__ui.has_selection = 0;
+ break;
+ }
+ }
+ break;
+
+ case STBTE__tool_rect:
+ if (STBTE__IS_HOT(id)) {
+ switch (stbte__ui.event) {
+ case STBTE__leftdown:
+ if (STBTE__INACTIVE())
+ stbte__activate_map(mapx,mapy);
+ break;
+ case STBTE__leftup:
+ if (STBTE__IS_MAP_ACTIVE()) {
+ stbte__fillrect(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy, 1);
+ stbte__activate(0);
+ }
+ break;
+ case STBTE__rightdown:
+ if (STBTE__INACTIVE())
+ stbte__activate_map(mapx,mapy);
+ break;
+ case STBTE__rightup:
+ if (STBTE__IS_MAP_ACTIVE()) {
+ stbte__fillrect(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy, 0);
+ stbte__activate(0);
+ }
+ break;
+ }
+ }
+ break;
+
+
+ case STBTE__tool_eyedrop:
+ switch (stbte__ui.event) {
+ case STBTE__leftdown:
+ if (STBTE__IS_HOT(id) && STBTE__INACTIVE())
+ stbte__eyedrop(tm,mapx,mapy);
+ break;
+ }
+ break;
+ }
+}
+
+static void stbte__start_paste(stbte_tilemap *tm)
+{
+ if (stbte__ui.has_copy) {
+ stbte__ui.pasting = 1;
+ stbte__activate(STBTE__ID(STBTE__toolbarB,3));
+ }
+}
+
+static void stbte__toolbar(stbte_tilemap *tm, int x0, int y0, int w, int h)
+{
+ int i;
+ int estimated_width = 13 * STBTE__num_tool + 8+8+ 120+4 - 30;
+ int x = x0 + w/2 - estimated_width/2;
+ int y = y0+1;
+
+ for (i=0; i < STBTE__num_tool; ++i) {
+ int highlight=0, disable=0;
+ highlight = (stbte__ui.tool == i);
+ if (i == STBTE__tool_undo || i == STBTE__tool_showgrid)
+ x += 8;
+ if (i == STBTE__tool_showgrid && stbte__ui.show_grid)
+ highlight = 1;
+ if (i == STBTE__tool_showlinks && stbte__ui.show_links)
+ highlight = 1;
+ if (i == STBTE__tool_fill)
+ continue;
+ #ifndef STBTE_ALLOW_LINK
+ if (i == STBTE__tool_link || i == STBTE__tool_showlinks)
+ disable = 1;
+ #endif
+ if (i == STBTE__tool_undo && !stbte__undo_available(tm))
+ disable = 1;
+ if (i == STBTE__tool_redo && !stbte__redo_available(tm))
+ disable = 1;
+ if (stbte__button_icon(STBTE__ctoolbar_button, toolchar[i], x, y, 13, STBTE__ID(STBTE__toolbarA, i), highlight, disable)) {
+ switch (i) {
+ case STBTE__tool_eyedrop:
+ stbte__ui.eyedrop_last_layer = tm->num_layers;
+
+ default:
+ stbte__ui.tool = i;
+ stbte__ui.has_selection = 0;
+ break;
+ case STBTE__tool_showlinks:
+ stbte__ui.show_links = !stbte__ui.show_links;
+ break;
+ case STBTE__tool_showgrid:
+ stbte__ui.show_grid = (stbte__ui.show_grid+1)%3;
+ break;
+ case STBTE__tool_undo:
+ stbte__undo(tm);
+ break;
+ case STBTE__tool_redo:
+ stbte__redo(tm);
+ break;
+ }
+ }
+ x += 13;
+ }
+
+ x += 8;
+ if (stbte__button(STBTE__ctoolbar_button, "cut" , x, y,10, 40, STBTE__ID(STBTE__toolbarB,0), 0, !stbte__ui.has_selection))
+ stbte__copy_cut(tm, 1);
+ x += 42;
+ if (stbte__button(STBTE__ctoolbar_button, "copy" , x, y, 5, 40, STBTE__ID(STBTE__toolbarB,1), 0, !stbte__ui.has_selection))
+ stbte__copy_cut(tm, 0);
+ x += 42;
+ if (stbte__button(STBTE__ctoolbar_button, "paste", x, y, 0, 40, STBTE__ID(STBTE__toolbarB,2), stbte__ui.pasting, !stbte__ui.has_copy))
+ stbte__start_paste(tm);
+}
+
+#define STBTE__TEXTCOLOR(n) stbte__color_table[n][STBTE__text][STBTE__idle]
+
+static int stbte__info_value(const char *label, int x, int y, int val, int digits, int id)
+{
+ if (stbte__ui.event == STBTE__paint) {
+ int off = 9-stbte__get_char_width(label[0]);
+ char text[16];
+ stbte__sprintf(text stbte__sizeof(text), label, digits, val);
+ stbte__draw_text_core(x+off,y, text, 999, STBTE__TEXTCOLOR(STBTE__cpanel),1);
+ }
+ if (id) {
+ x += 9+7*digits+4;
+ if (stbte__minibutton(STBTE__cmapsize, x,y, '+', STBTE__ID2(id,1,0)))
+ val += (stbte__ui.shift ? 10 : 1);
+ x += 9;
+ if (stbte__minibutton(STBTE__cmapsize, x,y, '-', STBTE__ID2(id,2,0)))
+ val -= (stbte__ui.shift ? 10 : 1);
+ if (val < 1) val = 1; else if (val > 4096) val = 4096;
+ }
+ return val;
+}
+
+static void stbte__info(stbte_tilemap *tm, int x0, int y0, int w, int h)
+{
+ int mode = stbte__ui.panel[STBTE__panel_info].mode;
+ int s = 11+7*tm->digits+4+15;
+ int x,y;
+ int in_region;
+
+ x = x0+2;
+ y = y0+2;
+ tm->max_x = stbte__info_value("w:%*d",x,y, tm->max_x, tm->digits, STBTE__ID(STBTE__info,0));
+ if (mode)
+ x += s;
+ else
+ y += 11;
+ tm->max_y = stbte__info_value("h:%*d",x,y, tm->max_y, tm->digits, STBTE__ID(STBTE__info,1));
+ x = x0+2;
+ y += 11;
+ in_region = (stbte__ui.hot_id & 127) == STBTE__map;
+ stbte__info_value(in_region ? "x:%*d" : "x:",x,y, (stbte__ui.hot_id>>19)&4095, tm->digits, 0);
+ if (mode)
+ x += s;
+ else
+ y += 11;
+ stbte__info_value(in_region ? "y:%*d" : "y:",x,y, (stbte__ui.hot_id>> 7)&4095, tm->digits, 0);
+ y += 15;
+ x = x0+2;
+ stbte__draw_text(x,y,"brush:",40,STBTE__TEXTCOLOR(STBTE__cpanel));
+ if (tm->cur_tile >= 0)
+ STBTE_DRAW_TILE(x+43,y-3,tm->tiles[tm->cur_tile].id,1,0);
+}
+
+static void stbte__layers(stbte_tilemap *tm, int x0, int y0, int w, int h)
+{
+ static const char *propmodes[3] = {
+ "default", "always", "never"
+ };
+ int num_rows;
+ int i, y, n;
+ int x1 = x0+w;
+ int y1 = y0+h;
+ int xoff = 20;
+
+ if (tm->has_layer_names) {
+ int side = stbte__ui.panel[STBTE__panel_layers].side;
+ xoff = stbte__region[side].width - 42;
+ xoff = (xoff < tm->layername_width + 10 ? xoff : tm->layername_width + 10);
+ }
+
+ x0 += 2;
+ y0 += 5;
+ if (!tm->has_layer_names) {
+ if (stbte__ui.event == STBTE__paint) {
+ stbte__draw_text(x0,y0, "Layers", w-4, STBTE__TEXTCOLOR(STBTE__cpanel));
+ }
+ y0 += 11;
+ }
+ num_rows = (y1-y0)/15;
+#ifndef STBTE_NO_PROPS
+ --num_rows;
+#endif
+ y = y0;
+ for (i=0; i < tm->num_layers; ++i) {
+ char text[3], *str = (char *) tm->layerinfo[i].name;
+ static char lockedchar[3] = { 'U', 'P', 'L' };
+ int locked = tm->layerinfo[i].locked;
+ int disabled = (tm->solo_layer >= 0 && tm->solo_layer != i);
+ if (i-tm->layer_scroll >= 0 && i-tm->layer_scroll < num_rows) {
+ if (str == NULL)
+ stbte__sprintf(str=text stbte__sizeof(text), "%2d", i+1);
+ if (stbte__button(STBTE__clayer_button, str, x0,y,(i+1<10)*2,xoff-2, STBTE__ID(STBTE__layer,i), tm->cur_layer==i,0))
+ tm->cur_layer = (tm->cur_layer == i ? -1 : i);
+ if (stbte__layerbutton(x0+xoff + 0,y+1,'H',STBTE__ID(STBTE__hide,i), tm->layerinfo[i].hidden,disabled,STBTE__clayer_hide))
+ tm->layerinfo[i].hidden = !tm->layerinfo[i].hidden;
+ if (stbte__layerbutton(x0+xoff + 12,y+1,lockedchar[locked],STBTE__ID(STBTE__lock,i), locked!=0,disabled,STBTE__clayer_lock))
+ tm->layerinfo[i].locked = (locked+1)%3;
+ if (stbte__layerbutton(x0+xoff + 24,y+1,'S',STBTE__ID(STBTE__solo,i), tm->solo_layer==i,0,STBTE__clayer_solo))
+ tm->solo_layer = (tm->solo_layer == i ? -1 : i);
+ y += 15;
+ }
+ }
+ stbte__scrollbar(x1-4, y0,y-2, &tm->layer_scroll, 0, tm->num_layers, num_rows, STBTE__ID(STBTE__scrollbar_id, STBTE__layer));
+#ifndef STBTE_NO_PROPS
+ n = stbte__text_width("prop:")+2;
+ stbte__draw_text(x0,y+2, "prop:", w, STBTE__TEXTCOLOR(STBTE__cpanel));
+ i = w - n - 4;
+ if (i > 50) i = 50;
+ if (stbte__button(STBTE__clayer_button, propmodes[tm->propmode], x0+n,y,0,i, STBTE__ID(STBTE__layer,256), 0,0))
+ tm->propmode = (tm->propmode+1)%3;
+#endif
+}
+
+static void stbte__categories(stbte_tilemap *tm, int x0, int y0, int w, int h)
+{
+ int s=11, x,y, i;
+ int num_rows = h / s;
+
+ w -= 4;
+ x = x0+2;
+ y = y0+4;
+ if (tm->category_scroll == 0) {
+ if (stbte__category_button("*ALL*", x,y, w, STBTE__ID(STBTE__categories, 65535), tm->cur_category == -1)) {
+ stbte__choose_category(tm, -1);
+ }
+ y += s;
+ }
+
+ for (i=0; i < tm->num_categories; ++i) {
+ if (i+1 - tm->category_scroll >= 0 && i+1 - tm->category_scroll < num_rows) {
+ if (y + 10 > y0+h)
+ return;
+ if (stbte__category_button(tm->categories[i], x,y,w, STBTE__ID(STBTE__categories,i), tm->cur_category == i))
+ stbte__choose_category(tm, i);
+ y += s;
+ }
+ }
+ stbte__scrollbar(x0+w, y0+4, y0+h-4, &tm->category_scroll, 0, tm->num_categories+1, num_rows, STBTE__ID(STBTE__scrollbar_id, STBTE__categories));
+}
+
+static void stbte__tile_in_palette(stbte_tilemap *tm, int x, int y, int slot)
+{
+ stbte__tileinfo *t = &tm->tiles[slot];
+ int x0=x, y0=y, x1 = x+tm->palette_spacing_x - 1, y1 = y+tm->palette_spacing_y;
+ int id = STBTE__ID(STBTE__palette, slot);
+ stbte__hittest(x0,y0,x1,y1, id);
+ switch (stbte__ui.event) {
+ case STBTE__paint:
+ stbte__draw_rect(x,y,x+tm->palette_spacing_x-1,y+tm->palette_spacing_x-1, STBTE_COLOR_TILEPALETTE_BACKGROUND);
+ STBTE_DRAW_TILE(x,y,id, slot == tm->cur_tile,0);
+ if (slot == tm->cur_tile)
+ stbte__draw_frame_delayed(x-1,y-1,x+tm->palette_spacing_x,y+tm->palette_spacing_y, STBTE_COLOR_TILEPALETTE_OUTLINE);
+ break;
+ default:
+ if (stbte__button_core(id))
+ tm->cur_tile = slot;
+ break;
+ }
+}
+
+static void stbte__palette_of_tiles(stbte_tilemap *tm, int x0, int y0, int w, int h)
+{
+ int i,x,y;
+ int num_vis_rows = (h-6) / tm->palette_spacing_y;
+ int num_columns = (w-2-6) / tm->palette_spacing_x;
+ int num_total_rows;
+ int column,row;
+ int x1 = x0+w, y1=y0+h;
+ x = x0+2;
+ y = y0+6;
+
+ if (num_columns == 0)
+ return;
+
+ num_total_rows = (tm->cur_palette_count + num_columns-1) / num_columns;
+
+ column = 0;
+ row = -tm->palette_scroll;
+ for (i=0; i < tm->num_tiles; ++i) {
+ stbte__tileinfo *t = &tm->tiles[i];
+
+
+ if (tm->cur_category >= 0 && t->category_id != tm->cur_category)
+ continue;
+
+
+ if (row >= 0 && row < num_vis_rows) {
+ x = x0 + 2 + tm->palette_spacing_x * column;
+ y = y0 + 6 + tm->palette_spacing_y * row;
+ stbte__tile_in_palette(tm,x,y,i);
+ }
+
+ ++column;
+ if (column == num_columns) {
+ column = 0;
+ ++row;
+ }
+ }
+ stbte__flush_delay();
+ stbte__scrollbar(x1-4, y0+6, y1-2, &tm->palette_scroll, 0, num_total_rows, num_vis_rows, STBTE__ID(STBTE__scrollbar_id, STBTE__palette));
+}
+
+static float stbte__saved;
+static void stbte__props_panel(stbte_tilemap *tm, int x0, int y0, int w, int h)
+{
+ int x1 = x0+w;
+ int i;
+ int y = y0 + 5, x = x0+2;
+ int slider_width = 60;
+ int mx,my;
+ float *p;
+ short *data;
+ if (!stbte__is_single_selection())
+ return;
+ mx = stbte__ui.select_x0;
+ my = stbte__ui.select_y0;
+ p = tm->props[my][mx];
+ data = tm->data[my][mx];
+ STBTE__NOTUSED(data);
+ for (i=0; i < STBTE_MAX_PROPERTIES; ++i) {
+ unsigned int n = STBTE_PROP_TYPE(i, data, p);
+ if (n) {
+ char *s = (char*) STBTE_PROP_NAME(i, data, p);
+ if (s == NULL) s = (char*) "";
+ switch (n & 3) {
+ case STBTE_PROP_bool: {
+ int flag = (int) p[i];
+ if (stbte__layerbutton(x,y, flag ? 'x' : ' ', STBTE__ID(STBTE__prop_flag,i), flag, 0, 2)) {
+ stbte__begin_undo(tm);
+ stbte__undo_record_prop_float(tm,mx,my,i,(float) flag);
+ p[i] = (float) !flag;
+ stbte__end_undo(tm);
+ }
+ stbte__draw_text(x+13,y+1,s,x1-(x+13)-2,STBTE__TEXTCOLOR(STBTE__cpanel));
+ y += 13;
+ break;
+ }
+ case STBTE_PROP_int: {
+ int a = (int) STBTE_PROP_MIN(i,data,p);
+ int b = (int) STBTE_PROP_MAX(i,data,p);
+ int v = (int) p[i] - a;
+ if (a+v != p[i] || v < 0 || v > b-a) {
+ if (v < 0) v = 0;
+ if (v > b-a) v = b-a;
+ p[i] = (float) (a+v);
+ }
+ switch (stbte__slider(x, slider_width, y+7, b-a, &v, STBTE__ID(STBTE__prop_int,i)))
+ {
+ case STBTE__begin:
+ stbte__saved = p[i];
+
+ case STBTE__change:
+ p[i] = (float) (a+v);
+ break;
+ case STBTE__end:
+ if (p[i] != stbte__saved) {
+ stbte__begin_undo(tm);
+ stbte__undo_record_prop_float(tm,mx,my,i,stbte__saved);
+ stbte__end_undo(tm);
+ }
+ break;
+ }
+ stbte__draw_text(x+slider_width+2,y+2, s, x1-1-(x+slider_width+2), STBTE__TEXTCOLOR(STBTE__cpanel));
+ y += 12;
+ break;
+ }
+ case STBTE_PROP_float: {
+ float a = (float) STBTE_PROP_MIN(i, data,p);
+ float b = (float) STBTE_PROP_MAX(i, data,p);
+ float c = STBTE_PROP_FLOAT_SCALE(i, data, p);
+ float old;
+ if (p[i] < a || p[i] > b) {
+
+ if (p[i] < a) p[i] = a;
+ if (p[i] > b) p[i] = b;
+ }
+ old = p[i];
+ switch (stbte__float_control(x, y, 50, a, b, c, "%8.4f", &p[i], STBTE__layer,STBTE__ID(STBTE__prop_float,i))) {
+ case STBTE__begin:
+ stbte__saved = old;
+ break;
+ case STBTE__end:
+ if (stbte__saved != p[i]) {
+ stbte__begin_undo(tm);
+ stbte__undo_record_prop_float(tm,mx,my,i, stbte__saved);
+ stbte__end_undo(tm);
+ }
+ break;
+ }
+ stbte__draw_text(x+53,y+1, s, x1-1-(x+53), STBTE__TEXTCOLOR(STBTE__cpanel));
+ y += 12;
+ break;
+ }
+ }
+ }
+ }
+}
+
+static int stbte__cp_mode, stbte__cp_aspect, stbte__save, stbte__cp_altered;
+#ifdef STBTE__COLORPICKER
+static int stbte__cp_state, stbte__cp_index, stbte__color_copy;
+static void stbte__dump_colorstate(void)
+{
+ int i,j,k;
+ printf("static int stbte__color_table[STBTE__num_color_modes][STBTE__num_color_aspects][STBTE__num_color_states] =\n");
+ printf("{\n");
+ printf(" {\n");
+ for (k=0; k < STBTE__num_color_modes; ++k) {
+ for (j=0; j < STBTE__num_color_aspects; ++j) {
+ printf(" { ");
+ for (i=0; i < STBTE__num_color_states; ++i) {
+ printf("0x%06x, ", stbte__color_table[k][j][i]);
+ }
+ printf("},\n");
+ }
+ if (k+1 < STBTE__num_color_modes)
+ printf(" }, {\n");
+ else
+ printf(" },\n");
+ }
+ printf("};\n");
+}
+
+static void stbte__colorpicker(int x0, int y0, int w, int h)
+{
+ int x1 = x0+w, y1 = y0+h, x,y, i;
+
+ x = x0+2; y = y0+6;
+
+ y += 5;
+ x += 8;
+
+
+ {
+ int color = stbte__color_table[stbte__cp_mode][stbte__cp_aspect][stbte__cp_index];
+ int rgb[3];
+ if (stbte__cp_altered && stbte__cp_index == STBTE__idle)
+ color = stbte__save;
+
+ if (stbte__minibutton(STBTE__cmapsize, x1-20,y+ 5, 'C', STBTE__ID2(STBTE__colorpick_id,4,0)))
+ stbte__color_copy = color;
+ if (stbte__minibutton(STBTE__cmapsize, x1-20,y+15, 'P', STBTE__ID2(STBTE__colorpick_id,4,1)))
+ color = stbte__color_copy;
+
+ rgb[0] = color >> 16; rgb[1] = (color>>8)&255; rgb[2] = color & 255;
+ for (i=0; i < 3; ++i) {
+ if (stbte__slider(x+8,64, y, 255, rgb+i, STBTE__ID2(STBTE__colorpick_id,3,i)) > 0)
+ stbte__dump_colorstate();
+ y += 15;
+ }
+ if (stbte__ui.event != STBTE__paint && stbte__ui.event != STBTE__tick)
+ stbte__color_table[stbte__cp_mode][stbte__cp_aspect][stbte__cp_index] = (rgb[0]<<16)|(rgb[1]<<8)|(rgb[2]);
+ }
+
+ y += 5;
+
+
+ x = x0+2+35;
+ if (stbte__ui.event == STBTE__paint) {
+ static char *states[] = { "idle", "over", "down", "down&over", "selected", "selected&over", "disabled" };
+ stbte__draw_text(x, y+1, states[stbte__cp_index], x1-x-1, 0xffffff);
+ }
+
+ x = x0+24; y += 12;
+
+ for (i=3; i >= 0; --i) {
+ int state = 0 != (stbte__cp_state & (1 << i));
+ if (stbte__layerbutton(x,y, "OASD"[i], STBTE__ID2(STBTE__colorpick_id, 0,i), state,0, STBTE__clayer_button)) {
+ stbte__cp_state ^= (1 << i);
+ stbte__cp_index = stbte__state_to_index[0][0][0][stbte__cp_state];
+ }
+ x += 16;
+ }
+ x = x0+2; y += 18;
+
+ for (i=0; i < 3; ++i) {
+ static char *labels[] = { "Base", "Edge", "Text" };
+ if (stbte__button(STBTE__ctoolbar_button, labels[i], x,y,0,36, STBTE__ID2(STBTE__colorpick_id,1,i), stbte__cp_aspect==i,0))
+ stbte__cp_aspect = i;
+ x += 40;
+ }
+
+ y += 18;
+ x = x0+2;
+
+ for (i=0; i < STBTE__num_color_modes; ++i) {
+ if (stbte__button(STBTE__ctoolbar_button, stbte__color_names[i], x, y, 0,80, STBTE__ID2(STBTE__colorpick_id,2,i), stbte__cp_mode == i,0))
+ stbte__cp_mode = i;
+ y += 12;
+ }
+
+
+ if (stbte__ui.event == STBTE__tick) {
+ stbte__save = stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle];
+ if ((stbte__ui.active_id & 127) != STBTE__colorpick_id) {
+ if ((stbte__ui.ms_time & 2047) < 200) {
+ stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle] ^= 0x1f1f1f;
+ stbte__cp_altered = 1;
+ }
+ }
+ }
+}
+#endif
+
+static void stbte__editor_traverse(stbte_tilemap *tm)
+{
+ int i,j,i0,j0,i1,j1,n;
+
+ if (tm == NULL)
+ return;
+ if (stbte__ui.x0 == stbte__ui.x1 || stbte__ui.y0 == stbte__ui.y1)
+ return;
+
+ stbte__prepare_tileinfo(tm);
+
+ stbte__compute_panel_locations(tm);
+
+ if (stbte__ui.event == STBTE__paint) {
+
+ stbte__draw_rect(stbte__ui.x0, stbte__ui.y0, stbte__ui.x1, stbte__ui.y1, STBTE_COLOR_TILEMAP_BORDER);
+
+ stbte__draw_rect(stbte__ui.x0 - tm->scroll_x, stbte__ui.y0 - tm->scroll_y,
+ stbte__ui.x0 - tm->scroll_x + tm->spacing_x * tm->max_x,
+ stbte__ui.y0 - tm->scroll_y + tm->spacing_y * tm->max_y, STBTE_COLOR_TILEMAP_BACKGROUND);
+ }
+
+
+
+ i0 = (tm->scroll_x - tm->spacing_x) / tm->spacing_x;
+ j0 = (tm->scroll_y - tm->spacing_y) / tm->spacing_y;
+ i1 = (tm->scroll_x + stbte__ui.x1 - stbte__ui.x0) / tm->spacing_x + 1;
+ j1 = (tm->scroll_y + stbte__ui.y1 - stbte__ui.y0) / tm->spacing_y + 1;
+
+ if (i0 < 0) i0 = 0;
+ if (j0 < 0) j0 = 0;
+ if (i1 > tm->max_x) i1 = tm->max_x;
+ if (j1 > tm->max_y) j1 = tm->max_y;
+
+ if (stbte__ui.event == STBTE__paint) {
+
+
+ for (n=0; n < tm->num_layers; ++n) {
+ for (j=j0; j < j1; ++j) {
+ for (i=i0; i < i1; ++i) {
+ int x = stbte__ui.x0 + i * tm->spacing_x - tm->scroll_x;
+ int y = stbte__ui.y0 + j * tm->spacing_y - tm->scroll_y;
+ stbte__tile_paint(tm, x, y, i, j, n);
+ }
+ }
+ if (n == 0 && stbte__ui.show_grid == 1) {
+ int x = stbte__ui.x0 + i0 * tm->spacing_x - tm->scroll_x;
+ int y = stbte__ui.y0 + j0 * tm->spacing_y - tm->scroll_y;
+ for (i=0; x < stbte__ui.x1 && i <= i1; ++i, x += tm->spacing_x)
+ stbte__draw_rect(x, stbte__ui.y0, x+1, stbte__ui.y1, STBTE_COLOR_GRID);
+ for (j=0; y < stbte__ui.y1 && j <= j1; ++j, y += tm->spacing_y)
+ stbte__draw_rect(stbte__ui.x0, y, stbte__ui.x1, y+1, STBTE_COLOR_GRID);
+ }
+ }
+ }
+
+ if (stbte__ui.event == STBTE__paint) {
+
+ if (stbte__ui.show_grid == 2) {
+ int x = stbte__ui.x0 + i0 * tm->spacing_x - tm->scroll_x;
+ int y = stbte__ui.y0 + j0 * tm->spacing_y - tm->scroll_y;
+ for (i=0; x < stbte__ui.x1 && i <= i1; ++i, x += tm->spacing_x)
+ stbte__draw_rect(x, stbte__ui.y0, x+1, stbte__ui.y1, STBTE_COLOR_GRID);
+ for (j=0; y < stbte__ui.y1 && j <= j1; ++j, y += tm->spacing_y)
+ stbte__draw_rect(stbte__ui.x0, y, stbte__ui.x1, y+1, STBTE_COLOR_GRID);
+ }
+ }
+
+ for (j=j0; j < j1; ++j) {
+ for (i=i0; i < i1; ++i) {
+ int x = stbte__ui.x0 + i * tm->spacing_x - tm->scroll_x;
+ int y = stbte__ui.y0 + j * tm->spacing_y - tm->scroll_y;
+ stbte__tile(tm, x, y, i, j);
+ }
+ }
+
+ if (stbte__ui.event == STBTE__paint) {
+
+ if (stbte__ui.has_selection) {
+ int x0,y0,x1,y1;
+ x0 = stbte__ui.x0 + (stbte__ui.select_x0 ) * tm->spacing_x - tm->scroll_x;
+ y0 = stbte__ui.y0 + (stbte__ui.select_y0 ) * tm->spacing_y - tm->scroll_y;
+ x1 = stbte__ui.x0 + (stbte__ui.select_x1 + 1) * tm->spacing_x - tm->scroll_x + 1;
+ y1 = stbte__ui.y0 + (stbte__ui.select_y1 + 1) * tm->spacing_y - tm->scroll_y + 1;
+ stbte__draw_frame(x0,y0,x1,y1, (stbte__ui.ms_time & 256 ? STBTE_COLOR_SELECTION_OUTLINE1 : STBTE_COLOR_SELECTION_OUTLINE2));
+ }
+
+ stbte__flush_delay();
+
+ #ifdef STBTE_ALLOW_LINK
+ if (stbte__ui.linking && STBTE__IS_MAP_HOT()) {
+ int x0,y0,x1,y1;
+ int color;
+ int ex = ((stbte__ui.hot_id >> 19) & 4095);
+ int ey = ((stbte__ui.hot_id >> 7) & 4095);
+ x0 = stbte__ui.x0 + (stbte__ui.sx ) * tm->spacing_x - tm->scroll_x + (tm->spacing_x>>1)+1;
+ y0 = stbte__ui.y0 + (stbte__ui.sy ) * tm->spacing_y - tm->scroll_y + (tm->spacing_y>>1)+1;
+ x1 = stbte__ui.x0 + (ex ) * tm->spacing_x - tm->scroll_x + (tm->spacing_x>>1)-1;
+ y1 = stbte__ui.y0 + (ey ) * tm->spacing_y - tm->scroll_y + (tm->spacing_y>>1)-1;
+ if (STBTE_ALLOW_LINK(tm->data[stbte__ui.sy][stbte__ui.sx], tm->props[stbte__ui.sy][stbte__ui.sx], tm->data[ey][ex], tm->props[ey][ex]))
+ color = STBTE_LINK_COLOR_DRAWING;
+ else
+ color = STBTE_LINK_COLOR_DISALLOWED;
+ stbte__draw_link(x0,y0,x1,y1, color);
+ }
+ #endif
+ }
+ stbte__flush_delay();
+
+
+ for (i=0; i < STBTE__num_panel; ++i) {
+ stbte__panel *p = &stbte__ui.panel[i];
+ if (stbte__ui.event == STBTE__paint) {
+ stbte__draw_box(p->x0,p->y0,p->x0+p->width,p->y0+p->height, STBTE__cpanel, STBTE__idle);
+ }
+
+ stbte__hittest(p->x0,p->y0,p->x0+p->width,p->y0+p->height, STBTE__ID2(STBTE__panel, i, 0));
+ switch (i) {
+ case STBTE__panel_toolbar:
+ if (stbte__ui.event == STBTE__paint)
+ stbte__draw_rect(p->x0,p->y0,p->x0+p->width,p->y0+p->height, stbte__color_table[STBTE__ctoolbar][STBTE__base][STBTE__idle]);
+ stbte__toolbar(tm,p->x0,p->y0,p->width,p->height);
+ break;
+ case STBTE__panel_info:
+ stbte__info(tm,p->x0,p->y0,p->width,p->height);
+ break;
+ case STBTE__panel_layers:
+ stbte__layers(tm,p->x0,p->y0,p->width,p->height);
+ break;
+ case STBTE__panel_categories:
+ stbte__categories(tm,p->x0,p->y0,p->width,p->height);
+ break;
+ case STBTE__panel_colorpick:
+#ifdef STBTE__COLORPICKER
+ stbte__colorpicker(p->x0,p->y0,p->width,p->height);
+#endif
+ break;
+ case STBTE__panel_tiles:
+
+ if (stbte__ui.event == STBTE__paint && p->side == stbte__ui.panel[STBTE__panel_categories].side)
+ stbte__draw_rect(p->x0+1,p->y0-1,p->x0+p->width-1,p->y0+1, stbte__color_table[STBTE__cpanel][STBTE__base][STBTE__idle]);
+ stbte__palette_of_tiles(tm,p->x0,p->y0,p->width,p->height);
+ break;
+ case STBTE__panel_props:
+ stbte__props_panel(tm,p->x0,p->y0,p->width,p->height);
+ break;
+ }
+
+ for (j=0; j < 2; ++j) {
+ int result;
+ if (i == STBTE__panel_toolbar) continue;
+ result = stbte__microbutton(p->x0+p->width - 1 - 2*4 + 4*j,p->y0+2,3, STBTE__ID2(STBTE__panel, i, j+1), STBTE__cpanel_sider+j);
+ if (result) {
+ switch (j) {
+ case 0: p->side = result > 0 ? STBTE__side_left : STBTE__side_right; break;
+ case 1: p->delta_height += result; break;
+ }
+ }
+ }
+ }
+
+ if (stbte__ui.panel[STBTE__panel_categories].delta_height < -5) stbte__ui.panel[STBTE__panel_categories].delta_height = -5;
+ if (stbte__ui.panel[STBTE__panel_layers ].delta_height < -5) stbte__ui.panel[STBTE__panel_layers ].delta_height = -5;
+
+
+
+ for (i=0; i < 2; ++i) {
+ if (stbte__region[i].active) {
+ int x = stbte__region[i].x;
+ int width;
+ if (i == STBTE__side_left)
+ width = stbte__ui.left_width , x += stbte__region[i].width + 1;
+ else
+ width = -stbte__ui.right_width, x -= 6;
+ if (stbte__microbutton_dragger(x, stbte__region[i].y+2, 5, STBTE__ID(STBTE__region,i), &width)) {
+
+ if (stbte__region[i].retracted == 0.0)
+ stbte__region[i].retracted = 0.01f;
+ else
+ stbte__region[i].retracted = 0.0;
+ }
+ if (i == STBTE__side_left)
+ stbte__ui.left_width = width;
+ else
+ stbte__ui.right_width = -width;
+ if (stbte__ui.event == STBTE__tick) {
+ if (stbte__region[i].retracted && stbte__region[i].retracted < 1.0f) {
+ stbte__region[i].retracted += stbte__ui.dt*4;
+ if (stbte__region[i].retracted > 1)
+ stbte__region[i].retracted = 1;
+ }
+ }
+ }
+ }
+
+ if (stbte__ui.event == STBTE__paint && stbte__ui.alert_msg) {
+ int w = stbte__text_width(stbte__ui.alert_msg);
+ int x = (stbte__ui.x0+stbte__ui.x1)/2;
+ int y = (stbte__ui.y0+stbte__ui.y1)*5/6;
+ stbte__draw_rect (x-w/2-4,y-8, x+w/2+4,y+8, 0x604020);
+ stbte__draw_frame(x-w/2-4,y-8, x+w/2+4,y+8, 0x906030);
+ stbte__draw_text (x-w/2,y-4, stbte__ui.alert_msg, w+1, 0xff8040);
+ }
+
+#ifdef STBTE_SHOW_CURSOR
+ if (stbte__ui.event == STBTE__paint)
+ stbte__draw_bitmap(stbte__ui.mx, stbte__ui.my, stbte__get_char_width(26), stbte__get_char_bitmap(26), 0xe0e0e0);
+#endif
+
+ if (stbte__ui.event == STBTE__tick && stbte__ui.alert_msg) {
+ stbte__ui.alert_timer -= stbte__ui.dt;
+ if (stbte__ui.alert_timer < 0) {
+ stbte__ui.alert_timer = 0;
+ stbte__ui.alert_msg = 0;
+ }
+ }
+
+ if (stbte__ui.event == STBTE__paint) {
+ stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle] = stbte__save;
+ stbte__cp_altered = 0;
+ }
+}
+
+static void stbte__do_event(stbte_tilemap *tm)
+{
+ stbte__ui.next_hot_id = 0;
+ stbte__editor_traverse(tm);
+ stbte__ui.hot_id = stbte__ui.next_hot_id;
+
+
+
+ if (stbte__ui.active_id) {
+ if (stbte__ui.event == STBTE__leftup || stbte__ui.event == STBTE__rightup) {
+ if (!stbte__ui.pasting) {
+ stbte__activate(0);
+ if (stbte__ui.undoing)
+ stbte__end_undo(tm);
+ stbte__ui.scrolling = 0;
+ stbte__ui.dragging = 0;
+ stbte__ui.linking = 0;
+ }
+ }
+ }
+
+
+
+
+ if (stbte__ui.pasting) {
+ if (STBTE__IS_MAP_HOT()) {
+
+ stbte__ui.paste_x = ((stbte__ui.hot_id >> 19) & 4095) - (stbte__ui.copy_width >> 1);
+ stbte__ui.paste_y = ((stbte__ui.hot_id >> 7) & 4095) - (stbte__ui.copy_height >> 1);
+ }
+ }
+ if (stbte__ui.dragging) {
+ if (STBTE__IS_MAP_HOT()) {
+ stbte__ui.drag_dest_x = ((stbte__ui.hot_id >> 19) & 4095) - stbte__ui.drag_offx;
+ stbte__ui.drag_dest_y = ((stbte__ui.hot_id >> 7) & 4095) - stbte__ui.drag_offy;
+ }
+ }
+}
+
+static void stbte__set_event(int event, int x, int y)
+{
+ stbte__ui.event = event;
+ stbte__ui.mx = x;
+ stbte__ui.my = y;
+ stbte__ui.dx = x - stbte__ui.last_mouse_x;
+ stbte__ui.dy = y - stbte__ui.last_mouse_y;
+ stbte__ui.last_mouse_x = x;
+ stbte__ui.last_mouse_y = y;
+ stbte__ui.accum_x += stbte__ui.dx;
+ stbte__ui.accum_y += stbte__ui.dy;
+}
+
+void stbte_draw(stbte_tilemap *tm)
+{
+ stbte__ui.event = STBTE__paint;
+ stbte__editor_traverse(tm);
+}
+
+void stbte_mouse_move(stbte_tilemap *tm, int x, int y, int shifted, int scrollkey)
+{
+ stbte__set_event(STBTE__mousemove, x,y);
+ stbte__ui.shift = shifted;
+ stbte__ui.scrollkey = scrollkey;
+ stbte__do_event(tm);
+}
+
+void stbte_mouse_button(stbte_tilemap *tm, int x, int y, int right, int down, int shifted, int scrollkey)
+{
+ static int events[2][2] = { { STBTE__leftup , STBTE__leftdown },
+ { STBTE__rightup, STBTE__rightdown } };
+ stbte__set_event(events[right][down], x,y);
+ stbte__ui.shift = shifted;
+ stbte__ui.scrollkey = scrollkey;
+
+ stbte__do_event(tm);
+}
+
+void stbte_mouse_wheel(stbte_tilemap *tm, int x, int y, int vscroll)
+{
+
+}
+
+void stbte_action(stbte_tilemap *tm, enum stbte_action act)
+{
+ switch (act) {
+ case STBTE_tool_select: stbte__ui.tool = STBTE__tool_select; break;
+ case STBTE_tool_brush: stbte__ui.tool = STBTE__tool_brush; break;
+ case STBTE_tool_erase: stbte__ui.tool = STBTE__tool_erase; break;
+ case STBTE_tool_rectangle: stbte__ui.tool = STBTE__tool_rect; break;
+ case STBTE_tool_eyedropper: stbte__ui.tool = STBTE__tool_eyedrop; break;
+ case STBTE_tool_link: stbte__ui.tool = STBTE__tool_link; break;
+ case STBTE_act_toggle_grid: stbte__ui.show_grid = (stbte__ui.show_grid+1) % 3; break;
+ case STBTE_act_toggle_links: stbte__ui.show_links ^= 1; break;
+ case STBTE_act_undo: stbte__undo(tm); break;
+ case STBTE_act_redo: stbte__redo(tm); break;
+ case STBTE_act_cut: stbte__copy_cut(tm, 1); break;
+ case STBTE_act_copy: stbte__copy_cut(tm, 0); break;
+ case STBTE_act_paste: stbte__start_paste(tm); break;
+ case STBTE_scroll_left: tm->scroll_x -= tm->spacing_x; break;
+ case STBTE_scroll_right: tm->scroll_x += tm->spacing_x; break;
+ case STBTE_scroll_up: tm->scroll_y -= tm->spacing_y; break;
+ case STBTE_scroll_down: tm->scroll_y += tm->spacing_y; break;
+ }
+}
+
+void stbte_tick(stbte_tilemap *tm, float dt)
+{
+ stbte__ui.event = STBTE__tick;
+ stbte__ui.dt = dt;
+ stbte__do_event(tm);
+ stbte__ui.ms_time += (int) (dt * 1024) + 1;
+}
+
+void stbte_mouse_sdl(stbte_tilemap *tm, const void *sdl_event, float xs, float ys, int xo, int yo)
+{
+#ifdef _SDL_H
+ SDL_Event *event = (SDL_Event *) sdl_event;
+ SDL_Keymod km = SDL_GetModState();
+ int shift = (km & KMOD_LCTRL) || (km & KMOD_RCTRL);
+ int scrollkey = 0 != SDL_GetKeyboardState(NULL)[SDL_SCANCODE_SPACE];
+ switch (event->type) {
+ case SDL_MOUSEMOTION:
+ stbte_mouse_move(tm, (int) (xs*event->motion.x+xo), (int) (ys*event->motion.y+yo), shift, scrollkey);
+ break;
+ case SDL_MOUSEBUTTONUP:
+ stbte_mouse_button(tm, (int) (xs*event->button.x+xo), (int) (ys*event->button.y+yo), event->button.button != SDL_BUTTON_LEFT, 0, shift, scrollkey);
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ stbte_mouse_button(tm, (int) (xs*event->button.x+xo), (int) (ys*event->button.y+yo), event->button.button != SDL_BUTTON_LEFT, 1, shift, scrollkey);
+ break;
+ case SDL_MOUSEWHEEL:
+ stbte_mouse_wheel(tm, stbte__ui.mx, stbte__ui.my, event->wheel.y);
+ break;
+ }
+#else
+ STBTE__NOTUSED(tm);
+ STBTE__NOTUSED(sdl_event);
+ STBTE__NOTUSED(xs);
+ STBTE__NOTUSED(ys);
+ STBTE__NOTUSED(xo);
+ STBTE__NOTUSED(yo);
+#endif
+}
+
+#endif
+
+/*
+------------------------------------------------------------------------------
+This software is available under 2 licenses -- choose whichever you prefer.
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+Copyright (c) 2017 Sean Barrett
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+------------------------------------------------------------------------------
+ALTERNATIVE B - Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+software, either in source code form or as a compiled binary, for any purpose,
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and to
+the detriment of our heirs and successors. We intend this dedication to be an
+overt act of relinquishment in perpetuity of all present and future rights to
+this software under copyright law.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+------------------------------------------------------------------------------
+*/
diff --git a/05/tests/parse_stb_truetype.h b/05/tests/parse_stb_truetype.h
index e582b43..ce520fa 100644
--- a/05/tests/parse_stb_truetype.h
+++ b/05/tests/parse_stb_truetype.h
@@ -1634,7 +1634,7 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s
stbtt_uint16 flags, gidx;
int comp_num_verts = 0, i;
stbtt_vertex *comp_verts = 0, *tmp = 0;
- float mtx[6] = {1,0,0,1,0,0}, m, n;
+ float mtx[6] = {0,0,0,0,0,0}, m, n;
flags = ttSHORT(comp); comp+=2;
gidx = ttSHORT(comp); comp+=2;
@@ -4282,7 +4282,7 @@ static int equal(float *a, float *b)
static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts)
{
int i;
- float orig[2], ray[2] = { 1, 0 };
+ float orig[2], ray[2] = { 0, 0 };
float y_frac;
int winding = 0;
@@ -4497,7 +4497,7 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc
float ax = x1-x0, ay = y1-y0;
float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;
float mx = x0 - sx, my = y0 - sy;
- float res[3] = {0.f,0.f,0.f};
+ float res[3] = {0,0,0};
float px,py,t,it,dist2;
float a_inv = precompute[i];
if (a_inv == 0.0) {