initial checkin
This commit is contained in:
parent
70171b8d2a
commit
e2caccb811
806
stb_c_lexer.h
Normal file
806
stb_c_lexer.h
Normal file
@ -0,0 +1,806 @@
|
|||||||
|
// stb_c_lexer.h 0.04 -- public domain Sean Barrett 2013
|
||||||
|
// lexer for making little C-like languages with recursive-descent parsers
|
||||||
|
//
|
||||||
|
// This file provides both the interface and the implementation.
|
||||||
|
// To instantiate the implementation,
|
||||||
|
// #define STB_C_LEXER_IMPLEMENTATION
|
||||||
|
// in *ONE* source file, before #including this file.
|
||||||
|
//
|
||||||
|
// The default configuration is fairly close to a C lexer, although
|
||||||
|
// suffixes on integer constants are not handled (you can override this).
|
||||||
|
//
|
||||||
|
// History:
|
||||||
|
// 0.04
|
||||||
|
// fix octal parsing bug
|
||||||
|
// 0.03
|
||||||
|
// added STB_C_LEX_DISCARD_PREPROCESSOR option
|
||||||
|
// refactor API to simplify (only one struct instead of two)
|
||||||
|
// change literal enum names to have 'lit' at the end
|
||||||
|
// 0.02
|
||||||
|
// first public release
|
||||||
|
//
|
||||||
|
// Status:
|
||||||
|
// - haven't tested compiling as C++
|
||||||
|
// - haven't tested the float parsing path
|
||||||
|
// - maybe tested "get_location" function (used for error reporting) in 0.03
|
||||||
|
// - haven't tested the non-default-config paths (e.g. non-stdlib)
|
||||||
|
// - only tested default-config paths by eyeballing output of self-parse
|
||||||
|
//
|
||||||
|
// - haven't implemented multiline strings
|
||||||
|
// - haven't implemented octal/hex character constants
|
||||||
|
// - haven't implemented support for unicode CLEX_char
|
||||||
|
// - need to expand error reporting so you don't just get "CLEX_parse_error"
|
||||||
|
|
||||||
|
#ifndef STB_C_LEXER_DEFINITIONS
|
||||||
|
// to change the default parsing rules, copy the following lines
|
||||||
|
// into your C/C++ file *before* including this, and then replace
|
||||||
|
// the Y's with N's for the ones you don't want.
|
||||||
|
// --BEGIN--
|
||||||
|
|
||||||
|
#define STB_C_LEX_C_DECIMAL_INTS Y // "0|[1-9][0-9]*" CLEX_intlit
|
||||||
|
#define STB_C_LEX_C_HEX_INTS Y // "0x[0-9a-fA-F]+" CLEX_intlit
|
||||||
|
#define STB_C_LEX_C_OCTAL_INTS Y // "[0-7]+" CLEX_intlit
|
||||||
|
#define STB_C_LEX_C_DECIMAL_FLOATS Y // "[0-9]*(.[0-9]*([eE]-?[0-9]+)?) CLEX_floatlit
|
||||||
|
#define STB_C_LEX_C_IDENTIFIERS Y // "[_a-zA-Z][_a-zA-Z0-9]*" CLEX_id
|
||||||
|
#define STB_C_LEX_C_DQ_STRINGS Y // double-quote-delimited strings with escapes CLEX_dqstring
|
||||||
|
#define STB_C_LEX_C_SQ_STRINGS N // single-quote-delimited strings with escapes CLEX_ssstring
|
||||||
|
#define STB_C_LEX_C_CHARS Y // single-quote-delimited character with escape CLEX_charlits
|
||||||
|
#define STB_C_LEX_C_COMMENTS Y // "/* comment */"
|
||||||
|
#define STB_C_LEX_CPP_COMMENTS Y // "// comment to end of line\n"
|
||||||
|
#define STB_C_LEX_C_COMPARISONS Y // "==" CLEX_eq "!=" CLEX_noteq "<=" CLEX_lesseq ">=" CLEX_greatereq
|
||||||
|
#define STB_C_LEX_C_LOGICAL Y // "&&" CLEX_andand "||" CLEX_oror
|
||||||
|
#define STB_C_LEX_C_SHIFTS Y // "<<" CLEX_shl ">>" CLEX_shr
|
||||||
|
#define STB_C_LEX_C_INCREMENTS Y // "++" CLEX_plusplus "--" CLEX_minusminus
|
||||||
|
#define STB_C_LEX_C_ARROW Y // "->" CLEX_arrow
|
||||||
|
#define STB_C_LEX_EQUAL_ARROW N // "=>" CLEX_eqarrow
|
||||||
|
#define STB_C_LEX_C_BITWISEEQ Y // "&=" CLEX_andeq "|=" CLEX_oreq "^=" CLEX_xoreq
|
||||||
|
#define STB_C_LEX_C_ARITHEQ Y // "+=" CLEX_pluseq "-=" CLEX_minuseq
|
||||||
|
// "*=" CLEX_muleq "/=" CLEX_diveq "%=" CLEX_modeq
|
||||||
|
// if both STB_C_LEX_SHIFTS & STB_C_LEX_ARITHEQ:
|
||||||
|
// "<<=" CLEX_shleq ">>=" CLEX_shreq
|
||||||
|
|
||||||
|
#define STB_C_LEX_PARSE_SUFFIXES N // letters after numbers are parsed as part of those numbers, and must be in suffix list below
|
||||||
|
#define STB_C_LEX_DECIMAL_SUFFIXES "" // decimal integer suffixes e.g. "uUlL" -- these are returned as-is in string storage
|
||||||
|
#define STB_C_LEX_HEX_SUFFIXES "" // e.g. "uUlL"
|
||||||
|
#define STB_C_LEX_OCTAL_SUFFIXES "" // e.g. "uUlL"
|
||||||
|
#define STB_C_LEX_FLOAT_SUFFIXES "" //
|
||||||
|
|
||||||
|
#define STB_C_LEX_0_IS_EOF N // if Y, ends parsing at '\0'; if N, returns '\0' as token
|
||||||
|
#define STB_C_LEX_INTEGERS_AS_DOUBLES N // parses integers as doubles so they can be larger than 'int', but only if STB_C_LEX_STDLIB==N
|
||||||
|
#define STB_C_LEX_MULTILINE_DSTRINGS N // allow newlines in double-quoted strings
|
||||||
|
#define STB_C_LEX_MULTILINE_SSTRINGS N // allow newlines in single-quoted strings
|
||||||
|
#define STB_C_LEX_USE_STDLIB Y // use strtod,strtol for parsing #s; otherwise inaccurate hack
|
||||||
|
#define STB_C_LEX_DOLLAR_IDENTIFIER Y // allow $ as an identifier character
|
||||||
|
#define STB_C_LEX_FLOAT_NO_DECIMAL Y // allow floats that have no decimal point if they have an exponent
|
||||||
|
|
||||||
|
#define STB_C_LEX_DEFINE_ALL_TOKEN_NAMES N // if Y, all CLEX_ token names are defined, even if never returned
|
||||||
|
// leaving it as N should help you catch config bugs
|
||||||
|
|
||||||
|
#define STB_C_LEX_DISCARD_PREPROCESSOR Y // discard C-preprocessor directives (e.g. after prepocess
|
||||||
|
// still have #line, #pragma, etc)
|
||||||
|
|
||||||
|
//#define STB_C_LEX_ISWHITE(str) ... // return length in bytes of first character if it is whitespace
|
||||||
|
|
||||||
|
#define STB_C_LEXER_DEFINITIONS // This line prevents the header file from replacing your definitions
|
||||||
|
// --END--
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef INCLUDE_STB_C_LEXER_H
|
||||||
|
#define INCLUDE_STB_C_LEXER_H
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
// lexer variables
|
||||||
|
char *input_stream;
|
||||||
|
char *eof;
|
||||||
|
char *parse_point;
|
||||||
|
char *string_storage;
|
||||||
|
int string_storage_len;
|
||||||
|
|
||||||
|
// lexer parse location for error messages
|
||||||
|
char *where_firstchar;
|
||||||
|
char *where_lastchar;
|
||||||
|
|
||||||
|
// lexer token variables
|
||||||
|
long token;
|
||||||
|
double real_number;
|
||||||
|
long int_number;
|
||||||
|
char *string;
|
||||||
|
int string_len;
|
||||||
|
} stb_lexer;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int line_number;
|
||||||
|
int line_offset;
|
||||||
|
} stb_lex_location;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern void stb_c_lexer_init(stb_lexer *lexer, const char *input_stream, const char *input_stream_end, char *string_store, int store_length);
|
||||||
|
// this function initialize the 'lexer' structure
|
||||||
|
// Input:
|
||||||
|
// - input_stream points to the file to parse, loaded into memory
|
||||||
|
// - input_stream_end points to the end of the file, or NULL if you use 0-for-EOF
|
||||||
|
// - string_store is storage the lexer can use for storing parsed strings and identifiers
|
||||||
|
// - store_length is the length of that storage
|
||||||
|
|
||||||
|
extern int stb_c_lexer_get_token(stb_lexer *lexer);
|
||||||
|
// this function returns non-zero if a token is parsed, or 0 if at EOF
|
||||||
|
// Output:
|
||||||
|
// - lexer->token is the token ID, which is unicode code point for a single-char token, < 0 for a multichar or eof or error
|
||||||
|
// - lexer->real_number is a double constant value for CLEX_floatlit, or CLEX_intlit if STB_C_LEX_INTEGERS_AS_DOUBLES
|
||||||
|
// - lexer->int_number is an integer constant for CLEX_intlit if !STB_C_LEX_INTEGERS_AS_DOUBLES, or character for CLEX_charlit
|
||||||
|
// - lexer->string is a 0-terminated string for CLEX_dqstring or CLEX_sqstring or CLEX_identifier
|
||||||
|
// - lexer->string_len is the byte length of lexer->string
|
||||||
|
|
||||||
|
extern void stb_c_lexer_get_location(const stb_lexer *lexer, const char *where, stb_lex_location *loc);
|
||||||
|
// this inefficient function returns the line number and character offset of a
|
||||||
|
// given location in the file as returned by stb_lex_token. Because it's inefficient,
|
||||||
|
// you should only call it for errors, not for every token.
|
||||||
|
// For error messages of invalid tokens, you typically want the location of the start
|
||||||
|
// of the token (which caused the token to be invalid). For bugs involving legit
|
||||||
|
// tokens, you can report the first or the range.
|
||||||
|
// Output:
|
||||||
|
// - loc->line_number is the line number in the file, counting from 1, of the location
|
||||||
|
// - loc->line_offset is the char-offset in the line, counting from 0, of the location
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // INCLUDE_STB_C_LEXER_H
|
||||||
|
|
||||||
|
#ifdef STB_C_LEXER_IMPLEMENTATION
|
||||||
|
|
||||||
|
#if defined(Y) || defined(N)
|
||||||
|
#error "Can only use stb_c_lex in contexts where the preprocessor symbols 'Y' and 'N' are not defined"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// Hacky definitions so we can easily #if on them
|
||||||
|
#define Y(x) 1
|
||||||
|
#define N(x) 0
|
||||||
|
|
||||||
|
#if STB_C_LEX_USE_STDLIB(x)
|
||||||
|
#define STB__CLEX_use_stdlib
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if STB_C_LEX_INTEGERS_AS_DOUBLES(x)
|
||||||
|
typedef double stb__clex_int;
|
||||||
|
#define intfield real_number
|
||||||
|
#define STB__clex_int_as_double
|
||||||
|
#else
|
||||||
|
typedef long stb__clex_int;
|
||||||
|
#define intfield int_number
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Convert these config options to simple conditional #defines so we can more
|
||||||
|
// easily test them once we've change the meaning of Y/N
|
||||||
|
|
||||||
|
#if STB_C_LEX_PARSE_SUFFIXES(x)
|
||||||
|
#define STB__clex_parse_suffixes
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if STB_C_LEX_C_DECIMAL_INTS(x) || STB_C_LEX_C_HEX_INTS(x) || STB_C_LEX_DEFINE_ALL_TOKEN_NAMES(x)
|
||||||
|
#define STB__clex_define_int
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (STB_C_LEX_C_ARITHEQ(x) && STB_C_LEX_C_SHIFTS(x)) || STB_C_LEX_DEFINE_ALL_TOKEN_NAMES(x)
|
||||||
|
#define STB__clex_define_shifts
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if STB_C_LEX_C_HEX_INTS(x)
|
||||||
|
#define STB__clex_hex_ints
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if STB_C_LEX_C_DECIMAL_INTS(x)
|
||||||
|
#define STB__clex_decimal_ints
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if STB_C_LEX_C_OCTAL_INTS(x)
|
||||||
|
#define STB__clex_octal_ints
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if STB_C_LEX_C_DECIMAL_FLOATS(x)
|
||||||
|
#define STB__clex_decimal_floats
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if STB_C_LEX_DISCARD_PREPROCESSOR(x)
|
||||||
|
#define STB__clex_discard_preprocessor
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Now pick a definition of Y/N that's conducive to
|
||||||
|
// defining the enum of token names.
|
||||||
|
#if STB_C_LEX_DEFINE_ALL_TOKEN_NAMES(x) || defined(STB_C_LEXER_SELF_TEST)
|
||||||
|
#undef N
|
||||||
|
#define N(a) Y(a)
|
||||||
|
#else
|
||||||
|
#undef N
|
||||||
|
#define N(a)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#undef Y
|
||||||
|
#define Y(a) a,
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
CLEX_eof = 256,
|
||||||
|
CLEX_parse_error,
|
||||||
|
|
||||||
|
#ifdef STB__clex_define_int
|
||||||
|
CLEX_intlit,
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STB_C_LEX_C_DECIMAL_FLOATS( CLEX_floatlit )
|
||||||
|
STB_C_LEX_C_IDENTIFIERS( CLEX_id )
|
||||||
|
STB_C_LEX_C_DQ_STRINGS( CLEX_dqstring )
|
||||||
|
STB_C_LEX_C_SQ_STRINGS( CLEX_sqstring )
|
||||||
|
STB_C_LEX_C_CHARS( CLEX_charlit )
|
||||||
|
STB_C_LEX_C_COMPARISONS( CLEX_eq )
|
||||||
|
STB_C_LEX_C_COMPARISONS( CLEX_noteq )
|
||||||
|
STB_C_LEX_C_COMPARISONS( CLEX_lesseq )
|
||||||
|
STB_C_LEX_C_COMPARISONS( CLEX_greatereq )
|
||||||
|
STB_C_LEX_C_LOGICAL( CLEX_andand )
|
||||||
|
STB_C_LEX_C_LOGICAL( CLEX_oror )
|
||||||
|
STB_C_LEX_C_SHIFTS( CLEX_shl )
|
||||||
|
STB_C_LEX_C_SHIFTS( CLEX_shr )
|
||||||
|
STB_C_LEX_C_INCREMENTS( CLEX_plusplus )
|
||||||
|
STB_C_LEX_C_INCREMENTS( CLEX_minusminus )
|
||||||
|
STB_C_LEX_C_ARITHEQ( CLEX_pluseq )
|
||||||
|
STB_C_LEX_C_ARITHEQ( CLEX_minuseq )
|
||||||
|
STB_C_LEX_C_ARITHEQ( CLEX_muleq )
|
||||||
|
STB_C_LEX_C_ARITHEQ( CLEX_diveq )
|
||||||
|
STB_C_LEX_C_ARITHEQ( CLEX_modeq )
|
||||||
|
STB_C_LEX_C_BITWISEEQ( CLEX_andeq )
|
||||||
|
STB_C_LEX_C_BITWISEEQ( CLEX_oreq )
|
||||||
|
STB_C_LEX_C_BITWISEEQ( CLEX_xoreq )
|
||||||
|
STB_C_LEX_C_ARROW( CLEX_arrow )
|
||||||
|
STB_C_LEX_EQUAL_ARROW( CLEX_eqarrow )
|
||||||
|
|
||||||
|
#ifdef STB__clex_define_shifts
|
||||||
|
CLEX_shleq, CLEX_shreq,
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CLEX_first_unused_token
|
||||||
|
|
||||||
|
#undef Y
|
||||||
|
#define Y(a) a
|
||||||
|
};
|
||||||
|
|
||||||
|
// Now for the rest of the file we'll use the basic definition where
|
||||||
|
// where Y expands to its contents and N expands to nothing
|
||||||
|
#undef N
|
||||||
|
#define N(a)
|
||||||
|
|
||||||
|
// API function
|
||||||
|
void stb_c_lexer_init(stb_lexer *lexer, const char *input_stream, const char *input_stream_end, char *string_store, int store_length)
|
||||||
|
{
|
||||||
|
lexer->input_stream = (char *) input_stream;
|
||||||
|
lexer->eof = (char *) input_stream_end;
|
||||||
|
lexer->parse_point = (char *) input_stream;
|
||||||
|
lexer->string_storage = string_store;
|
||||||
|
lexer->string_storage_len = store_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// API function
|
||||||
|
void stb_c_lexer_get_location(const stb_lexer *lexer, const char *where, stb_lex_location *loc)
|
||||||
|
{
|
||||||
|
char *p = lexer->input_stream;
|
||||||
|
int line_number = 1;
|
||||||
|
int char_offset = 0;
|
||||||
|
while (*p) {
|
||||||
|
if (*p == '\n' || *p == '\r') {
|
||||||
|
p += (p[0]+p[1] == '\r'+'\n' ? 2 : 1); // skip newline
|
||||||
|
line_number += 1;
|
||||||
|
char_offset = 0;
|
||||||
|
} else {
|
||||||
|
++p;
|
||||||
|
++char_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loc->line_number = line_number;
|
||||||
|
loc->line_offset = char_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// main helper function for returning a parsed token
|
||||||
|
static int stb__clex_token(stb_lexer *lexer, int token, char *start, char *end)
|
||||||
|
{
|
||||||
|
lexer->token = token;
|
||||||
|
lexer->where_firstchar = start;
|
||||||
|
lexer->where_lastchar = end;
|
||||||
|
lexer->parse_point = end+1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper function for returning eof
|
||||||
|
static int stb__clex_eof(stb_lexer *lexer)
|
||||||
|
{
|
||||||
|
lexer->token = CLEX_eof;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stb__clex_iswhite(int x)
|
||||||
|
{
|
||||||
|
return x == ' ' || x == '\t' || x == '\r' || x == '\n' || x == '\f';
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *stb__strchr(const char *str, int ch)
|
||||||
|
{
|
||||||
|
for (; *str; ++str)
|
||||||
|
if (*str == ch)
|
||||||
|
return str;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse suffixes at the end of a number
|
||||||
|
static int stb__clex_parse_suffixes(stb_lexer *lexer, long tokenid, char *start, char *cur, const char *suffixes)
|
||||||
|
{
|
||||||
|
#ifdef STB__clex_parse_suffixes
|
||||||
|
lexer->string = lexer->string_storage;
|
||||||
|
lexer->string_len = 0;
|
||||||
|
|
||||||
|
while ((*cur >= 'a' && *cur <= 'z') || (*cur >= 'A' && *cur <= 'Z')) {
|
||||||
|
if (stb__strchr(suffixes, *cur) == 0)
|
||||||
|
return stb__clex_token(lexer, CLEX_parse_error, start, cur);
|
||||||
|
if (lexer->string_len+1 >= lexer->string_storage_len)
|
||||||
|
return stb__clex_token(lexer, CLEX_parse_error, start, cur);
|
||||||
|
lexer->string[lexer->string_len++] = *cur++;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
suffixes = suffixes; // attempt to suppress warnings
|
||||||
|
#endif
|
||||||
|
return stb__clex_token(lexer, tokenid, start, cur-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef STB__CLEX_use_stdlib
|
||||||
|
static double stb__clex_parse_float(char *p, char **q)
|
||||||
|
{
|
||||||
|
double value=0;
|
||||||
|
while (*p >= '0' && *p <= '9')
|
||||||
|
value = value*10 + (*p++ - '0');
|
||||||
|
if (*p == '.') {
|
||||||
|
double powten=1, addend = 0;
|
||||||
|
++p;
|
||||||
|
while (*p >= '0' && *p <= '9') {
|
||||||
|
addend = addend + 10*(*p++ - '0');
|
||||||
|
powten *= 10;
|
||||||
|
}
|
||||||
|
value += addend / powten;
|
||||||
|
}
|
||||||
|
if (*p == 'e' || *p == 'E') {
|
||||||
|
int sign = p[1] == '-';
|
||||||
|
int exponent=0;
|
||||||
|
double pow10=1;
|
||||||
|
p += 1+sign;
|
||||||
|
while (*p >= '0' && *p <= '9')
|
||||||
|
exponent = exponent*10 + (*p++ - '0');
|
||||||
|
// can't use pow() from stdlib, so do it slow way
|
||||||
|
while (exponent-- > 0)
|
||||||
|
pow10 *= 10;
|
||||||
|
if (sign)
|
||||||
|
value /= pow10;
|
||||||
|
else
|
||||||
|
value *= pow10;
|
||||||
|
}
|
||||||
|
*q = p;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int stb__clex_parse_char(char *p, char **q)
|
||||||
|
{
|
||||||
|
if (*p == '\\') {
|
||||||
|
*q = p+2; // tentatively guess we'll parse two characters
|
||||||
|
switch(p[1]) {
|
||||||
|
case '\\': return '\\';
|
||||||
|
case '\'': return '\'';
|
||||||
|
case '"': return '"';
|
||||||
|
case 't': return '\t';
|
||||||
|
case 'f': return '\f';
|
||||||
|
case 'n': return '\n';
|
||||||
|
case 'r': return '\r';
|
||||||
|
case '0': return '\0'; // @TODO ocatal constants
|
||||||
|
case 'x': case 'X': return -1; // @TODO hex constants
|
||||||
|
case 'u': return -1; // @TODO unicode constants
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*q = p+1;
|
||||||
|
return (unsigned char) *p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stb__clex_parse_string(stb_lexer *lexer, char *p, int type)
|
||||||
|
{
|
||||||
|
char *start = p;
|
||||||
|
char delim = *p++; // grab the " or ' for later matching
|
||||||
|
char *out = lexer->string_storage;
|
||||||
|
char *outend = lexer->string_storage + lexer->string_storage_len;
|
||||||
|
while (*p != delim) {
|
||||||
|
int n;
|
||||||
|
if (*p == '\\') {
|
||||||
|
char *q;
|
||||||
|
n = stb__clex_parse_char(p, &q);
|
||||||
|
if (n < 0)
|
||||||
|
return stb__clex_token(lexer, CLEX_parse_error, start, q);
|
||||||
|
p = q;
|
||||||
|
} else {
|
||||||
|
// @OPTIMIZE: could speed this up by looping-while-not-backslash
|
||||||
|
n = (unsigned char) *p++;
|
||||||
|
}
|
||||||
|
if (out+1 > outend)
|
||||||
|
return stb__clex_token(lexer, CLEX_parse_error, start, p);
|
||||||
|
// @TODO expand unicode escapes to UTF8
|
||||||
|
*out++ = (char) n;
|
||||||
|
}
|
||||||
|
*out = 0;
|
||||||
|
lexer->string = lexer->string_storage;
|
||||||
|
lexer->string_len = out - lexer->string_storage;
|
||||||
|
return stb__clex_token(lexer, type, start, p+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int stb_c_lexer_get_token(stb_lexer *lexer)
|
||||||
|
{
|
||||||
|
char *p = lexer->parse_point;
|
||||||
|
|
||||||
|
// skip whitespace and comments
|
||||||
|
for (;;) {
|
||||||
|
#ifdef STB_C_LEX_ISWHITE
|
||||||
|
while (p != lexer->stream_end) {
|
||||||
|
int n;
|
||||||
|
n = STB_C_LEX_ISWHITE(p);
|
||||||
|
if (n == 0) break;
|
||||||
|
if (lexer->eof && lexer+n > lexer->eof)
|
||||||
|
return stb__clex_token(tok, CLEX_parse_error, p,lexer->eof-1);
|
||||||
|
p += n;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
while (p != lexer->eof && stb__clex_iswhite(*p))
|
||||||
|
++p;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STB_C_LEX_CPP_COMMENTS(
|
||||||
|
if (p != lexer->eof && p[0] == '/' && p[1] == '/') {
|
||||||
|
while (p != lexer->eof && *p != '\r' && *p != '\n')
|
||||||
|
++p;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
STB_C_LEX_C_COMMENTS(
|
||||||
|
if (p != lexer->eof && p[0] == '/' && p[1] == '*') {
|
||||||
|
char *start = p;
|
||||||
|
p += 2;
|
||||||
|
while (p != lexer->eof && (p[0] != '*' || p[1] != '/'))
|
||||||
|
++p;
|
||||||
|
if (p == lexer->eof)
|
||||||
|
return stb__clex_token(lexer, CLEX_parse_error, start, p-1);
|
||||||
|
p += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
#ifdef STB__clex_discard_preprocessor
|
||||||
|
// @TODO this discards everything after a '#', regardless
|
||||||
|
// of where in the line the # is, rather than requiring it
|
||||||
|
// be at the start. (because this parser doesn't otherwise
|
||||||
|
// check for line breaks!)
|
||||||
|
if (p != lexer->eof && p[0] == '#') {
|
||||||
|
while (p != lexer->eof && *p != '\r' && *p != '\n')
|
||||||
|
++p;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p == lexer->eof)
|
||||||
|
return stb__clex_eof(lexer);
|
||||||
|
|
||||||
|
switch (*p) {
|
||||||
|
default:
|
||||||
|
if ( (*p >= 'a' && *p <= 'z')
|
||||||
|
|| (*p >= 'A' && *p <= 'Z')
|
||||||
|
|| *p == '_' || (unsigned char) *p >= 128 // >= 128 is UTF8 char
|
||||||
|
STB_C_LEX_DOLLAR_IDENTIFIER( || *p == '$' ) )
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
lexer->string = lexer->string_storage;
|
||||||
|
lexer->string_len = n;
|
||||||
|
do {
|
||||||
|
if (n+1 >= lexer->string_storage_len)
|
||||||
|
return stb__clex_token(lexer, CLEX_parse_error, p, p+n);
|
||||||
|
lexer->string[n] = p[n];
|
||||||
|
++n;
|
||||||
|
} while (
|
||||||
|
(p[n] >= 'a' && p[n] <= 'z')
|
||||||
|
|| (p[n] >= 'A' && p[n] <= 'Z')
|
||||||
|
|| (p[n] >= '0' && p[n] <= '9') // allow digits in middle of identifier
|
||||||
|
|| p[n] == '_' || (unsigned char) p[n] >= 128
|
||||||
|
STB_C_LEX_DOLLAR_IDENTIFIER( || p[n] == '$' )
|
||||||
|
);
|
||||||
|
lexer->string[n] = 0;
|
||||||
|
return stb__clex_token(lexer, CLEX_id, p, p+n-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for EOF
|
||||||
|
STB_C_LEX_0_IS_EOF(
|
||||||
|
if (*p == 0)
|
||||||
|
return stb__clex_eof(tok);
|
||||||
|
)
|
||||||
|
|
||||||
|
single_char:
|
||||||
|
// not an identifier, return the character as itself
|
||||||
|
return stb__clex_token(lexer, *p, p, p);
|
||||||
|
|
||||||
|
case '+':
|
||||||
|
if (p+1 != lexer->eof) {
|
||||||
|
STB_C_LEX_C_INCREMENTS(if (p[1] == '+') return stb__clex_token(lexer, CLEX_plusplus, p,p+1);)
|
||||||
|
STB_C_LEX_C_ARITHEQ( if (p[1] == '=') return stb__clex_token(lexer, CLEX_pluseq , p,p+1);)
|
||||||
|
}
|
||||||
|
goto single_char;
|
||||||
|
case '-':
|
||||||
|
if (p+1 != lexer->eof) {
|
||||||
|
STB_C_LEX_C_INCREMENTS(if (p[1] == '-') return stb__clex_token(lexer, CLEX_minusminus, p,p+1);)
|
||||||
|
STB_C_LEX_C_ARITHEQ( if (p[1] == '=') return stb__clex_token(lexer, CLEX_minuseq , p,p+1);)
|
||||||
|
STB_C_LEX_C_ARROW( if (p[1] == '>') return stb__clex_token(lexer, CLEX_arrow , p,p+1);)
|
||||||
|
}
|
||||||
|
goto single_char;
|
||||||
|
case '&':
|
||||||
|
if (p+1 != lexer->eof) {
|
||||||
|
STB_C_LEX_C_LOGICAL( if (p[1] == '&') return stb__clex_token(lexer, CLEX_andand, p,p+1);)
|
||||||
|
STB_C_LEX_C_BITWISEEQ(if (p[1] == '=') return stb__clex_token(lexer, CLEX_andeq , p,p+1);)
|
||||||
|
}
|
||||||
|
goto single_char;
|
||||||
|
case '|':
|
||||||
|
if (p+1 != lexer->eof) {
|
||||||
|
STB_C_LEX_C_LOGICAL( if (p[1] == '|') return stb__clex_token(lexer, CLEX_oror, p,p+1);)
|
||||||
|
STB_C_LEX_C_BITWISEEQ(if (p[1] == '=') return stb__clex_token(lexer, CLEX_oreq, p,p+1);)
|
||||||
|
}
|
||||||
|
goto single_char;
|
||||||
|
case '=':
|
||||||
|
if (p+1 != lexer->eof) {
|
||||||
|
STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_eq, p,p+1);)
|
||||||
|
STB_C_LEX_EQUAL_ARROW( if (p[1] == '>') return stb__clex_token(lexer, CLEX_eqarrow, p,p+1);)
|
||||||
|
}
|
||||||
|
goto single_char;
|
||||||
|
case '!':
|
||||||
|
STB_C_LEX_C_COMPARISONS(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_noteq, p,p+1);)
|
||||||
|
goto single_char;
|
||||||
|
case '^':
|
||||||
|
STB_C_LEX_C_BITWISEEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_xoreq, p,p+1));
|
||||||
|
goto single_char;
|
||||||
|
case '%':
|
||||||
|
STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_modeq, p,p+1));
|
||||||
|
goto single_char;
|
||||||
|
case '*':
|
||||||
|
STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_muleq, p,p+1));
|
||||||
|
goto single_char;
|
||||||
|
case '/':
|
||||||
|
STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_diveq, p,p+1));
|
||||||
|
goto single_char;
|
||||||
|
case '<':
|
||||||
|
if (p+1 != lexer->eof) {
|
||||||
|
STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_lesseq, p,p+1);)
|
||||||
|
STB_C_LEX_C_SHIFTS( if (p[1] == '<') {
|
||||||
|
STB_C_LEX_C_ARITHEQ(if (p+2 != lexer->eof && p[2] == '=')
|
||||||
|
return stb__clex_token(lexer, CLEX_shleq, p,p+2);)
|
||||||
|
return stb__clex_token(lexer, CLEX_shl, p,p+1);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
goto single_char;
|
||||||
|
case '>':
|
||||||
|
if (p+1 != lexer->eof) {
|
||||||
|
STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_greatereq, p,p+1);)
|
||||||
|
STB_C_LEX_C_SHIFTS( if (p[1] == '>') {
|
||||||
|
STB_C_LEX_C_ARITHEQ(if (p+2 != lexer->eof && p[2] == '=')
|
||||||
|
return stb__clex_token(lexer, CLEX_shreq, p,p+2);)
|
||||||
|
return stb__clex_token(lexer, CLEX_shr, p,p+1);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
goto single_char;
|
||||||
|
|
||||||
|
case '"':
|
||||||
|
STB_C_LEX_C_DQ_STRINGS(return stb__clex_parse_string(lexer, p, CLEX_dqstring);)
|
||||||
|
goto single_char;
|
||||||
|
case '\'':
|
||||||
|
STB_C_LEX_C_SQ_STRINGS(return stb__clex_parse_string(lexer, p, CLEX_sqstring);)
|
||||||
|
STB_C_LEX_C_CHARS(
|
||||||
|
{
|
||||||
|
char *start = p;
|
||||||
|
lexer->int_number = stb__clex_parse_char(p+1, &p);
|
||||||
|
if (lexer->int_number < 0)
|
||||||
|
return stb__clex_token(lexer, CLEX_parse_error, start,start);
|
||||||
|
if (p == lexer->eof || *p != '\'')
|
||||||
|
return stb__clex_token(lexer, CLEX_parse_error, start,p);
|
||||||
|
return stb__clex_token(lexer, CLEX_charlit, start, p+1);
|
||||||
|
})
|
||||||
|
goto single_char;
|
||||||
|
|
||||||
|
case '0':
|
||||||
|
#ifdef STB__clex_hex_ints
|
||||||
|
if (p+1 != lexer->eof) {
|
||||||
|
if (p[1] == 'x' || p[1] == 'X') {
|
||||||
|
char *q = p+2;
|
||||||
|
#ifdef STB__CLEX_use_stdlib
|
||||||
|
lexer->int_number = strtol((char *) p, (char **) q, 16);
|
||||||
|
#else
|
||||||
|
stb__clex_int n=0;
|
||||||
|
while (q != lexer->eof) {
|
||||||
|
if (*q >= '0' && *q <= '9')
|
||||||
|
n = n*16 + (*q - '0');
|
||||||
|
else if (*q >= 'a' && *q <= 'f')
|
||||||
|
n = n*16 + (*q - 'a') + 10;
|
||||||
|
else if (*q >= 'A' && *q <= 'F')
|
||||||
|
n = n*16 + (*q - 'A') + 10;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
++q;
|
||||||
|
}
|
||||||
|
lexer->int_field = n; // int_field is macro that expands to real_number/int_number depending on type of n
|
||||||
|
#endif
|
||||||
|
if (q == p+2)
|
||||||
|
return stb__clex_token(lexer, CLEX_parse_error, p-2,p-1);
|
||||||
|
return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_HEX_SUFFIXES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // STB__clex_hex_ints
|
||||||
|
// can't test for octal because we might parse '0.0' as float or as '0' '.' '0',
|
||||||
|
// so have to do float first
|
||||||
|
|
||||||
|
/* FALL THROUGH */
|
||||||
|
case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
|
||||||
|
|
||||||
|
#ifdef STB__clex_decimal_floats
|
||||||
|
{
|
||||||
|
char *q = p;
|
||||||
|
while (q != lexer->eof && (*q >= '0' && *q <= '9'))
|
||||||
|
++q;
|
||||||
|
if (q != lexer->eof) {
|
||||||
|
if (*q == '.' STB_C_LEX_FLOAT_NO_DECIMAL(|| *q == 'e' || *q == 'E')) {
|
||||||
|
#ifdef STB__CLEX_use_stdlib
|
||||||
|
lexer->real_number = strtod((char *) p, (char**) &q);
|
||||||
|
#else
|
||||||
|
lexer->real_number = stb__clex_parse_float(p, &q);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return stb__clex_parse_suffixes(lexer, CLEX_floatlit, p,q, STB_C_LEX_FLOAT_SUFFIXES);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // STB__clex_decimal_floats
|
||||||
|
|
||||||
|
#ifdef STB__clex_octal_ints
|
||||||
|
if (p[0] == '0') {
|
||||||
|
char *q = p;
|
||||||
|
#ifdef STB__CLEX_use_stdlib
|
||||||
|
lexer->int_number = strtol((char *) p, (char **) &q, 8);
|
||||||
|
#else
|
||||||
|
stb__clex_int n=0;
|
||||||
|
while (q != lexer->eof) {
|
||||||
|
if (*q >= '0' && *q <= '7')
|
||||||
|
n = n*8 + (q - '0');
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
++q;
|
||||||
|
}
|
||||||
|
if (q != lexer->eof && (*q == '8' || *q=='9'))
|
||||||
|
return stb__clex_token(tok, CLEX_parse_error, p, q);
|
||||||
|
lexer->int_field = n;
|
||||||
|
#endif
|
||||||
|
return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_OCTAL_SUFFIXES);
|
||||||
|
}
|
||||||
|
#endif // STB__clex_octal_ints
|
||||||
|
|
||||||
|
#ifdef STB__clex_decimal_ints
|
||||||
|
{
|
||||||
|
char *q = p;
|
||||||
|
#ifdef STB__CLEX_use_stdlib
|
||||||
|
lexer->int_number = strtol((char *) p, (char **) &q, 10);
|
||||||
|
#else
|
||||||
|
stb__clex_int n=0;
|
||||||
|
while (q != lexer->eof) {
|
||||||
|
if (*q >= '0' && *q <= '9')
|
||||||
|
n = n*10 + (q - '0');
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
++q;
|
||||||
|
}
|
||||||
|
lexer->int_field = n;
|
||||||
|
#endif
|
||||||
|
return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_OCTAL_SUFFIXES);
|
||||||
|
}
|
||||||
|
#endif // STB__clex_decimal_ints
|
||||||
|
goto single_char;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // STB_C_LEXER_IMPLEMENTATION
|
||||||
|
|
||||||
|
#ifdef STB_C_LEXER_SELF_TEST
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
static void print_token(stb_lexer *lexer)
|
||||||
|
{
|
||||||
|
switch (lexer->token) {
|
||||||
|
case CLEX_id : printf("_%s", lexer->string); break;
|
||||||
|
case CLEX_eq : printf("=="); break;
|
||||||
|
case CLEX_noteq : printf("!="); break;
|
||||||
|
case CLEX_lesseq : printf("<="); break;
|
||||||
|
case CLEX_greatereq : printf(">="); break;
|
||||||
|
case CLEX_andand : printf("&&"); break;
|
||||||
|
case CLEX_oror : printf("||"); break;
|
||||||
|
case CLEX_shl : printf("<<"); break;
|
||||||
|
case CLEX_shr : printf(">>"); break;
|
||||||
|
case CLEX_plusplus : printf("++"); break;
|
||||||
|
case CLEX_minusminus: printf("--"); break;
|
||||||
|
case CLEX_arrow : printf("->"); break;
|
||||||
|
case CLEX_andeq : printf("&="); break;
|
||||||
|
case CLEX_oreq : printf("|="); break;
|
||||||
|
case CLEX_xoreq : printf("^="); break;
|
||||||
|
case CLEX_pluseq : printf("+="); break;
|
||||||
|
case CLEX_minuseq : printf("-="); break;
|
||||||
|
case CLEX_muleq : printf("*="); break;
|
||||||
|
case CLEX_diveq : printf("/="); break;
|
||||||
|
case CLEX_modeq : printf("%%="); break;
|
||||||
|
case CLEX_shleq : printf("<<="); break;
|
||||||
|
case CLEX_shreq : printf(">>="); break;
|
||||||
|
case CLEX_eqarrow : printf("=>"); break;
|
||||||
|
case CLEX_dqstring : printf("\"%s\"", lexer->string); break;
|
||||||
|
case CLEX_sqstring : printf("'\"%s\"'", lexer->string); break;
|
||||||
|
case CLEX_charlit : printf("'%s'", lexer->string); break;
|
||||||
|
#if defined(STB__clex_int_as_double) && !defined(STB__CLEX_use_stdlib)
|
||||||
|
case CLEX_intlit : printf("#%g", lexer->real_number); break;
|
||||||
|
#else
|
||||||
|
case CLEX_intlit : printf("#%ld", lexer->int_number); break;
|
||||||
|
#endif
|
||||||
|
case CLEX_floatlit : printf("%g", lexer->real_number); break;
|
||||||
|
default:
|
||||||
|
if (lexer->token >= 0 && lexer->token < 256)
|
||||||
|
printf("%c", (int) lexer->token);
|
||||||
|
else {
|
||||||
|
printf("<<<UNKNOWN TOKEN %ld >>>\n", lexer->token);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Force a test
|
||||||
|
of parsing
|
||||||
|
multiline comments */
|
||||||
|
|
||||||
|
/*/ comment /*/
|
||||||
|
/**/ extern /**/
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
FILE *f = fopen("stb_c_lexer.h", "rb");
|
||||||
|
char *text = (char *) malloc(1 << 20);
|
||||||
|
int len = f ? fread(text, 1, 1<<20, f) : -1;
|
||||||
|
stb_lexer lex;
|
||||||
|
if (len < 0) {
|
||||||
|
fprintf(stderr, "Error opening file\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
stb_c_lexer_init(&lex, text, text+len, (char *) malloc(1<<16), 1<<16);
|
||||||
|
while (stb_c_lexer_get_token(&lex)) {
|
||||||
|
if (lex.token == CLEX_parse_error) {
|
||||||
|
printf("\n<<<PARSE ERROR>>>\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
print_token(&lex);
|
||||||
|
printf(" ");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
4673
stb_image.c
Normal file
4673
stb_image.c
Normal file
File diff suppressed because it is too large
Load Diff
511
stb_image_write.h
Normal file
511
stb_image_write.h
Normal file
@ -0,0 +1,511 @@
|
|||||||
|
/* stbiw-0.92 - public domain - http://nothings.org/stb/stb_image_write.h
|
||||||
|
writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010
|
||||||
|
no warranty implied; use at your own risk
|
||||||
|
|
||||||
|
|
||||||
|
Before including,
|
||||||
|
|
||||||
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
|
||||||
|
in the file that you want to have the implementation.
|
||||||
|
|
||||||
|
|
||||||
|
ABOUT:
|
||||||
|
|
||||||
|
This header file is a library for writing images to C stdio. It could be
|
||||||
|
adapted to write to memory or a general streaming interface; let me know.
|
||||||
|
|
||||||
|
The PNG output is not optimal; it is 20-50% larger than the file
|
||||||
|
written by a decent optimizing implementation. This library is designed
|
||||||
|
for source code compactness and simplicitly, not optimal image file size
|
||||||
|
or run-time performance.
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
|
||||||
|
There are three functions, one for each image file format:
|
||||||
|
|
||||||
|
int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
|
||||||
|
int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
|
||||||
|
int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
|
||||||
|
|
||||||
|
Each function returns 0 on failure and non-0 on success.
|
||||||
|
|
||||||
|
The functions create an image file defined by the parameters. The image
|
||||||
|
is a rectangle of pixels stored from left-to-right, top-to-bottom.
|
||||||
|
Each pixel contains 'comp' channels of data stored interleaved with 8-bits
|
||||||
|
per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is
|
||||||
|
monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall.
|
||||||
|
The *data pointer points to the first byte of the top-left-most pixel.
|
||||||
|
For PNG, "stride_in_bytes" is the distance in bytes from the first byte of
|
||||||
|
a row of pixels to the first byte of the next row of pixels.
|
||||||
|
|
||||||
|
PNG creates output files with the same number of components as the input.
|
||||||
|
The BMP and TGA formats expand Y to RGB in the file format. BMP does not
|
||||||
|
output alpha.
|
||||||
|
|
||||||
|
PNG supports writing rectangles of data even when the bytes storing rows of
|
||||||
|
data are not consecutive in memory (e.g. sub-rectangles of a larger image),
|
||||||
|
by supplying the stride between the beginning of adjacent rows. The other
|
||||||
|
formats do not. (Thus you cannot write a native-format BMP through the BMP
|
||||||
|
writer, both because it is in BGR order and because it may have padding
|
||||||
|
at the end of the line.)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDE_STB_IMAGE_WRITE_H
|
||||||
|
#define INCLUDE_STB_IMAGE_WRITE_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
|
||||||
|
extern int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
|
||||||
|
extern int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif//INCLUDE_STB_IMAGE_WRITE_H
|
||||||
|
|
||||||
|
#ifdef STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
typedef unsigned int stbiw_uint32;
|
||||||
|
typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1];
|
||||||
|
|
||||||
|
static void writefv(FILE *f, const char *fmt, va_list v)
|
||||||
|
{
|
||||||
|
while (*fmt) {
|
||||||
|
switch (*fmt++) {
|
||||||
|
case ' ': break;
|
||||||
|
case '1': { unsigned char x = (unsigned char) va_arg(v, int); fputc(x,f); break; }
|
||||||
|
case '2': { int x = va_arg(v,int); unsigned char b[2];
|
||||||
|
b[0] = (unsigned char) x; b[1] = (unsigned char) (x>>8);
|
||||||
|
fwrite(b,2,1,f); break; }
|
||||||
|
case '4': { stbiw_uint32 x = va_arg(v,int); unsigned char b[4];
|
||||||
|
b[0]=(unsigned char)x; b[1]=(unsigned char)(x>>8);
|
||||||
|
b[2]=(unsigned char)(x>>16); b[3]=(unsigned char)(x>>24);
|
||||||
|
fwrite(b,4,1,f); break; }
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write3(FILE *f, unsigned char a, unsigned char b, unsigned char c)
|
||||||
|
{
|
||||||
|
unsigned char arr[3];
|
||||||
|
arr[0] = a, arr[1] = b, arr[2] = c;
|
||||||
|
fwrite(arr, 3, 1, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad)
|
||||||
|
{
|
||||||
|
unsigned char bg[3] = { 255, 0, 255}, px[3];
|
||||||
|
stbiw_uint32 zero = 0;
|
||||||
|
int i,j,k, j_end;
|
||||||
|
|
||||||
|
if (y <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (vdir < 0)
|
||||||
|
j_end = -1, j = y-1;
|
||||||
|
else
|
||||||
|
j_end = y, j = 0;
|
||||||
|
|
||||||
|
for (; j != j_end; j += vdir) {
|
||||||
|
for (i=0; i < x; ++i) {
|
||||||
|
unsigned char *d = (unsigned char *) data + (j*x+i)*comp;
|
||||||
|
if (write_alpha < 0)
|
||||||
|
fwrite(&d[comp-1], 1, 1, f);
|
||||||
|
switch (comp) {
|
||||||
|
case 1:
|
||||||
|
case 2: write3(f, d[0],d[0],d[0]);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
if (!write_alpha) {
|
||||||
|
// composite against pink background
|
||||||
|
for (k=0; k < 3; ++k)
|
||||||
|
px[k] = bg[k] + ((d[k] - bg[k]) * d[3])/255;
|
||||||
|
write3(f, px[1-rgb_dir],px[1],px[1+rgb_dir]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
case 3:
|
||||||
|
write3(f, d[1-rgb_dir],d[1],d[1+rgb_dir]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (write_alpha > 0)
|
||||||
|
fwrite(&d[comp-1], 1, 1, f);
|
||||||
|
}
|
||||||
|
fwrite(&zero,scanline_pad,1,f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, void *data, int alpha, int pad, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
if (y < 0 || x < 0) return 0;
|
||||||
|
f = fopen(filename, "wb");
|
||||||
|
if (f) {
|
||||||
|
va_list v;
|
||||||
|
va_start(v, fmt);
|
||||||
|
writefv(f, fmt, v);
|
||||||
|
va_end(v);
|
||||||
|
write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
return f != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data)
|
||||||
|
{
|
||||||
|
int pad = (-x*3) & 3;
|
||||||
|
return outfile(filename,-1,-1,x,y,comp,(void *) data,0,pad,
|
||||||
|
"11 4 22 4" "4 44 22 444444",
|
||||||
|
'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header
|
||||||
|
40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header
|
||||||
|
}
|
||||||
|
|
||||||
|
int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data)
|
||||||
|
{
|
||||||
|
int has_alpha = !(comp & 1);
|
||||||
|
return outfile(filename, -1,-1, x, y, comp, (void *) data, has_alpha, 0,
|
||||||
|
"111 221 2222 11", 0,0,2, 0,0,0, 0,0,x,y, 24+8*has_alpha, 8*has_alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
// stretchy buffer; stbi__sbpush() == vector<>::push_back() -- stbi__sbcount() == vector<>::size()
|
||||||
|
#define stbi__sbraw(a) ((int *) (a) - 2)
|
||||||
|
#define stbi__sbm(a) stbi__sbraw(a)[0]
|
||||||
|
#define stbi__sbn(a) stbi__sbraw(a)[1]
|
||||||
|
|
||||||
|
#define stbi__sbneedgrow(a,n) ((a)==0 || stbi__sbn(a)+n >= stbi__sbm(a))
|
||||||
|
#define stbi__sbmaybegrow(a,n) (stbi__sbneedgrow(a,(n)) ? stbi__sbgrow(a,n) : 0)
|
||||||
|
#define stbi__sbgrow(a,n) stbi__sbgrowf((void **) &(a), (n), sizeof(*(a)))
|
||||||
|
|
||||||
|
#define stbi__sbpush(a, v) (stbi__sbmaybegrow(a,1), (a)[stbi__sbn(a)++] = (v))
|
||||||
|
#define stbi__sbcount(a) ((a) ? stbi__sbn(a) : 0)
|
||||||
|
#define stbi__sbfree(a) ((a) ? free(stbi__sbraw(a)),0 : 0)
|
||||||
|
|
||||||
|
static void *stbi__sbgrowf(void **arr, int increment, int itemsize)
|
||||||
|
{
|
||||||
|
int m = *arr ? 2*stbi__sbm(*arr)+increment : increment+1;
|
||||||
|
void *p = realloc(*arr ? stbi__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2);
|
||||||
|
assert(p);
|
||||||
|
if (p) {
|
||||||
|
if (!*arr) ((int *) p)[1] = 0;
|
||||||
|
*arr = (void *) ((int *) p + 2);
|
||||||
|
stbi__sbm(*arr) = m;
|
||||||
|
}
|
||||||
|
return *arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char *stbi__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount)
|
||||||
|
{
|
||||||
|
while (*bitcount >= 8) {
|
||||||
|
stbi__sbpush(data, (unsigned char) *bitbuffer);
|
||||||
|
*bitbuffer >>= 8;
|
||||||
|
*bitcount -= 8;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stbi__zlib_bitrev(int code, int codebits)
|
||||||
|
{
|
||||||
|
int res=0;
|
||||||
|
while (codebits--) {
|
||||||
|
res = (res << 1) | (code & 1);
|
||||||
|
code >>= 1;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int stbi__zlib_countm(unsigned char *a, unsigned char *b, int limit)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i=0; i < limit && i < 258; ++i)
|
||||||
|
if (a[i] != b[i]) break;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int stbi__zhash(unsigned char *data)
|
||||||
|
{
|
||||||
|
stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16);
|
||||||
|
hash ^= hash << 3;
|
||||||
|
hash += hash >> 5;
|
||||||
|
hash ^= hash << 4;
|
||||||
|
hash += hash >> 17;
|
||||||
|
hash ^= hash << 25;
|
||||||
|
hash += hash >> 6;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define stbi__zlib_flush() (out = stbi__zlib_flushf(out, &bitbuf, &bitcount))
|
||||||
|
#define stbi__zlib_add(code,codebits) \
|
||||||
|
(bitbuf |= (code) << bitcount, bitcount += (codebits), stbi__zlib_flush())
|
||||||
|
#define stbi__zlib_huffa(b,c) stbi__zlib_add(stbi__zlib_bitrev(b,c),c)
|
||||||
|
// default huffman tables
|
||||||
|
#define stbi__zlib_huff1(n) stbi__zlib_huffa(0x30 + (n), 8)
|
||||||
|
#define stbi__zlib_huff2(n) stbi__zlib_huffa(0x190 + (n)-144, 9)
|
||||||
|
#define stbi__zlib_huff3(n) stbi__zlib_huffa(0 + (n)-256,7)
|
||||||
|
#define stbi__zlib_huff4(n) stbi__zlib_huffa(0xc0 + (n)-280,8)
|
||||||
|
#define stbi__zlib_huff(n) ((n) <= 143 ? stbi__zlib_huff1(n) : (n) <= 255 ? stbi__zlib_huff2(n) : (n) <= 279 ? stbi__zlib_huff3(n) : stbi__zlib_huff4(n))
|
||||||
|
#define stbi__zlib_huffb(n) ((n) <= 143 ? stbi__zlib_huff1(n) : stbi__zlib_huff2(n))
|
||||||
|
|
||||||
|
#define stbi__ZHASH 16384
|
||||||
|
|
||||||
|
unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality)
|
||||||
|
{
|
||||||
|
static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 };
|
||||||
|
static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 };
|
||||||
|
static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 };
|
||||||
|
static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 };
|
||||||
|
unsigned int bitbuf=0;
|
||||||
|
int i,j, bitcount=0;
|
||||||
|
unsigned char *out = NULL;
|
||||||
|
unsigned char **hash_table[stbi__ZHASH]; // 64KB on the stack!
|
||||||
|
if (quality < 5) quality = 5;
|
||||||
|
|
||||||
|
stbi__sbpush(out, 0x78); // DEFLATE 32K window
|
||||||
|
stbi__sbpush(out, 0x5e); // FLEVEL = 1
|
||||||
|
stbi__zlib_add(1,1); // BFINAL = 1
|
||||||
|
stbi__zlib_add(1,2); // BTYPE = 1 -- fixed huffman
|
||||||
|
|
||||||
|
for (i=0; i < stbi__ZHASH; ++i)
|
||||||
|
hash_table[i] = NULL;
|
||||||
|
|
||||||
|
i=0;
|
||||||
|
while (i < data_len-3) {
|
||||||
|
// hash next 3 bytes of data to be compressed
|
||||||
|
int h = stbi__zhash(data+i)&(stbi__ZHASH-1), best=3;
|
||||||
|
unsigned char *bestloc = 0;
|
||||||
|
unsigned char **hlist = hash_table[h];
|
||||||
|
int n = stbi__sbcount(hlist);
|
||||||
|
for (j=0; j < n; ++j) {
|
||||||
|
if (hlist[j]-data > i-32768) { // if entry lies within window
|
||||||
|
int d = stbi__zlib_countm(hlist[j], data+i, data_len-i);
|
||||||
|
if (d >= best) best=d,bestloc=hlist[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// when hash table entry is too long, delete half the entries
|
||||||
|
if (hash_table[h] && stbi__sbn(hash_table[h]) == 2*quality) {
|
||||||
|
memcpy(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality);
|
||||||
|
stbi__sbn(hash_table[h]) = quality;
|
||||||
|
}
|
||||||
|
stbi__sbpush(hash_table[h],data+i);
|
||||||
|
|
||||||
|
if (bestloc) {
|
||||||
|
// "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal
|
||||||
|
h = stbi__zhash(data+i+1)&(stbi__ZHASH-1);
|
||||||
|
hlist = hash_table[h];
|
||||||
|
n = stbi__sbcount(hlist);
|
||||||
|
for (j=0; j < n; ++j) {
|
||||||
|
if (hlist[j]-data > i-32767) {
|
||||||
|
int e = stbi__zlib_countm(hlist[j], data+i+1, data_len-i-1);
|
||||||
|
if (e > best) { // if next match is better, bail on current match
|
||||||
|
bestloc = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestloc) {
|
||||||
|
int d = data+i - bestloc; // distance back
|
||||||
|
assert(d <= 32767 && best <= 258);
|
||||||
|
for (j=0; best > lengthc[j+1]-1; ++j);
|
||||||
|
stbi__zlib_huff(j+257);
|
||||||
|
if (lengtheb[j]) stbi__zlib_add(best - lengthc[j], lengtheb[j]);
|
||||||
|
for (j=0; d > distc[j+1]-1; ++j);
|
||||||
|
stbi__zlib_add(stbi__zlib_bitrev(j,5),5);
|
||||||
|
if (disteb[j]) stbi__zlib_add(d - distc[j], disteb[j]);
|
||||||
|
i += best;
|
||||||
|
} else {
|
||||||
|
stbi__zlib_huffb(data[i]);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// write out final bytes
|
||||||
|
for (;i < data_len; ++i)
|
||||||
|
stbi__zlib_huffb(data[i]);
|
||||||
|
stbi__zlib_huff(256); // end of block
|
||||||
|
// pad with 0 bits to byte boundary
|
||||||
|
while (bitcount)
|
||||||
|
stbi__zlib_add(0,1);
|
||||||
|
|
||||||
|
for (i=0; i < stbi__ZHASH; ++i)
|
||||||
|
(void) stbi__sbfree(hash_table[i]);
|
||||||
|
|
||||||
|
{
|
||||||
|
// compute adler32 on input
|
||||||
|
unsigned int i=0, s1=1, s2=0, blocklen = data_len % 5552;
|
||||||
|
int j=0;
|
||||||
|
while (j < data_len) {
|
||||||
|
for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1;
|
||||||
|
s1 %= 65521, s2 %= 65521;
|
||||||
|
j += blocklen;
|
||||||
|
blocklen = 5552;
|
||||||
|
}
|
||||||
|
stbi__sbpush(out, (unsigned char) (s2 >> 8));
|
||||||
|
stbi__sbpush(out, (unsigned char) s2);
|
||||||
|
stbi__sbpush(out, (unsigned char) (s1 >> 8));
|
||||||
|
stbi__sbpush(out, (unsigned char) s1);
|
||||||
|
}
|
||||||
|
*out_len = stbi__sbn(out);
|
||||||
|
// make returned pointer freeable
|
||||||
|
memmove(stbi__sbraw(out), out, *out_len);
|
||||||
|
return (unsigned char *) stbi__sbraw(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int stbi__crc32(unsigned char *buffer, int len)
|
||||||
|
{
|
||||||
|
static unsigned int crc_table[256];
|
||||||
|
unsigned int crc = ~0u;
|
||||||
|
int i,j;
|
||||||
|
if (crc_table[1] == 0)
|
||||||
|
for(i=0; i < 256; i++)
|
||||||
|
for (crc_table[i]=i, j=0; j < 8; ++j)
|
||||||
|
crc_table[i] = (crc_table[i] >> 1) ^ (crc_table[i] & 1 ? 0xedb88320 : 0);
|
||||||
|
for (i=0; i < len; ++i)
|
||||||
|
crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)];
|
||||||
|
return ~crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define stbi__wpng4(o,a,b,c,d) ((o)[0]=(unsigned char)(a),(o)[1]=(unsigned char)(b),(o)[2]=(unsigned char)(c),(o)[3]=(unsigned char)(d),(o)+=4)
|
||||||
|
#define stbi__wp32(data,v) stbi__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v));
|
||||||
|
#define stbi__wptag(data,s) stbi__wpng4(data, s[0],s[1],s[2],s[3])
|
||||||
|
|
||||||
|
static void stbi__wpcrc(unsigned char **data, int len)
|
||||||
|
{
|
||||||
|
unsigned int crc = stbi__crc32(*data - len - 4, len+4);
|
||||||
|
stbi__wp32(*data, crc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char stbi__paeth(int a, int b, int c)
|
||||||
|
{
|
||||||
|
int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c);
|
||||||
|
if (pa <= pb && pa <= pc) return (unsigned char) a;
|
||||||
|
if (pb <= pc) return (unsigned char) b;
|
||||||
|
return (unsigned char) c;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len)
|
||||||
|
{
|
||||||
|
int ctype[5] = { -1, 0, 4, 2, 6 };
|
||||||
|
unsigned char sig[8] = { 137,80,78,71,13,10,26,10 };
|
||||||
|
unsigned char *out,*o, *filt, *zlib;
|
||||||
|
signed char *line_buffer;
|
||||||
|
int i,j,k,p,zlen;
|
||||||
|
|
||||||
|
if (stride_bytes == 0)
|
||||||
|
stride_bytes = x * n;
|
||||||
|
|
||||||
|
filt = (unsigned char *) malloc((x*n+1) * y); if (!filt) return 0;
|
||||||
|
line_buffer = (signed char *) malloc(x * n); if (!line_buffer) { free(filt); return 0; }
|
||||||
|
for (j=0; j < y; ++j) {
|
||||||
|
static int mapping[] = { 0,1,2,3,4 };
|
||||||
|
static int firstmap[] = { 0,1,0,5,6 };
|
||||||
|
int *mymap = j ? mapping : firstmap;
|
||||||
|
int best = 0, bestval = 0x7fffffff;
|
||||||
|
for (p=0; p < 2; ++p) {
|
||||||
|
for (k= p?best:0; k < 5; ++k) {
|
||||||
|
int type = mymap[k],est=0;
|
||||||
|
unsigned char *z = pixels + stride_bytes*j;
|
||||||
|
for (i=0; i < n; ++i)
|
||||||
|
switch (type) {
|
||||||
|
case 0: line_buffer[i] = z[i]; break;
|
||||||
|
case 1: line_buffer[i] = z[i]; break;
|
||||||
|
case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break;
|
||||||
|
case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break;
|
||||||
|
case 4: line_buffer[i] = (signed char) (z[i] - stbi__paeth(0,z[i-stride_bytes],0)); break;
|
||||||
|
case 5: line_buffer[i] = z[i]; break;
|
||||||
|
case 6: line_buffer[i] = z[i]; break;
|
||||||
|
}
|
||||||
|
for (i=n; i < x*n; ++i) {
|
||||||
|
switch (type) {
|
||||||
|
case 0: line_buffer[i] = z[i]; break;
|
||||||
|
case 1: line_buffer[i] = z[i] - z[i-n]; break;
|
||||||
|
case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break;
|
||||||
|
case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break;
|
||||||
|
case 4: line_buffer[i] = z[i] - stbi__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break;
|
||||||
|
case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break;
|
||||||
|
case 6: line_buffer[i] = z[i] - stbi__paeth(z[i-n], 0,0); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (p) break;
|
||||||
|
for (i=0; i < x*n; ++i)
|
||||||
|
est += abs((signed char) line_buffer[i]);
|
||||||
|
if (est < bestval) { bestval = est; best = k; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// when we get here, best contains the filter type, and line_buffer contains the data
|
||||||
|
filt[j*(x*n+1)] = (unsigned char) best;
|
||||||
|
memcpy(filt+j*(x*n+1)+1, line_buffer, x*n);
|
||||||
|
}
|
||||||
|
free(line_buffer);
|
||||||
|
zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory
|
||||||
|
free(filt);
|
||||||
|
if (!zlib) return 0;
|
||||||
|
|
||||||
|
// each tag requires 12 bytes of overhead
|
||||||
|
out = (unsigned char *) malloc(8 + 12+13 + 12+zlen + 12);
|
||||||
|
if (!out) return 0;
|
||||||
|
*out_len = 8 + 12+13 + 12+zlen + 12;
|
||||||
|
|
||||||
|
o=out;
|
||||||
|
memcpy(o,sig,8); o+= 8;
|
||||||
|
stbi__wp32(o, 13); // header length
|
||||||
|
stbi__wptag(o, "IHDR");
|
||||||
|
stbi__wp32(o, x);
|
||||||
|
stbi__wp32(o, y);
|
||||||
|
*o++ = 8;
|
||||||
|
*o++ = (unsigned char) ctype[n];
|
||||||
|
*o++ = 0;
|
||||||
|
*o++ = 0;
|
||||||
|
*o++ = 0;
|
||||||
|
stbi__wpcrc(&o,13);
|
||||||
|
|
||||||
|
stbi__wp32(o, zlen);
|
||||||
|
stbi__wptag(o, "IDAT");
|
||||||
|
memcpy(o, zlib, zlen); o += zlen; free(zlib);
|
||||||
|
stbi__wpcrc(&o, zlen);
|
||||||
|
|
||||||
|
stbi__wp32(o,0);
|
||||||
|
stbi__wptag(o, "IEND");
|
||||||
|
stbi__wpcrc(&o,0);
|
||||||
|
|
||||||
|
assert(o == out + *out_len);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
int len;
|
||||||
|
unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len);
|
||||||
|
if (!png) return 0;
|
||||||
|
f = fopen(filename, "wb");
|
||||||
|
if (!f) { free(png); return 0; }
|
||||||
|
fwrite(png, 1, len, f);
|
||||||
|
fclose(f);
|
||||||
|
free(png);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif // STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
|
||||||
|
/* Revision history
|
||||||
|
|
||||||
|
0.92 (2010-08-01)
|
||||||
|
casts to unsigned char to fix warnings
|
||||||
|
0.91 (2010-07-17)
|
||||||
|
first public release
|
||||||
|
0.90 first internal release
|
||||||
|
*/
|
175
stb_perlin.h
Normal file
175
stb_perlin.h
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
// stb_perlin.h -- perlin noise -- v0.2
|
||||||
|
// public domain single-file C implementation by Sean Barrett
|
||||||
|
//
|
||||||
|
// to create the implementation,
|
||||||
|
// #define STB_PERLIN_IMPLEMENTATION
|
||||||
|
// in *one* C/CPP file that includes this file.
|
||||||
|
|
||||||
|
|
||||||
|
// Documentation:
|
||||||
|
//
|
||||||
|
// float stb_perlin_noise3( float x,
|
||||||
|
// float y,
|
||||||
|
// float z,
|
||||||
|
// int x_wrap=0,
|
||||||
|
// int y_wrap=0,
|
||||||
|
// int z_wrap=0)
|
||||||
|
//
|
||||||
|
// This function computes a random value at the coordinate (x,y,z).
|
||||||
|
// Adjacent random values are continuous but the noise fluctuates
|
||||||
|
// its randomness with period 1, i.e. takes on wholly unrelated values
|
||||||
|
// at integer points. Specifically, this implements Ken Perlin's
|
||||||
|
// revised noise function from 2002.
|
||||||
|
//
|
||||||
|
// The "wrap" parameters can be used to create wraparound noise that
|
||||||
|
// wraps at powers of two. The numbers MUST be powers of two. Specify
|
||||||
|
// 0 to mean "don't care". (The noise always wraps every 256 due
|
||||||
|
// details of the implementation, even if you ask for larger or no
|
||||||
|
// wrapping.)
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" float stb_perlin_noise3(float x, float y, float z, int x_wrap=0, int y_wrap=0, int z_wrap=0);
|
||||||
|
#else
|
||||||
|
extern float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef STB_PERLIN_IMPLEMENTATION
|
||||||
|
|
||||||
|
#include <math.h> // floor()
|
||||||
|
|
||||||
|
// not same permutation table as Perlin's reference to avoid copyright issues;
|
||||||
|
// Perlin's table can be found at http://mrl.nyu.edu/~perlin/noise/
|
||||||
|
// @OPTIMIZE: should this be unsigned char instead of int for cache?
|
||||||
|
static int stb__perlin_randtab[512] =
|
||||||
|
{
|
||||||
|
23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123,
|
||||||
|
152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72,
|
||||||
|
175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240,
|
||||||
|
8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57,
|
||||||
|
225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233,
|
||||||
|
94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172,
|
||||||
|
165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243,
|
||||||
|
65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122,
|
||||||
|
26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76,
|
||||||
|
250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246,
|
||||||
|
132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3,
|
||||||
|
91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231,
|
||||||
|
38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221,
|
||||||
|
131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62,
|
||||||
|
27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135,
|
||||||
|
61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5,
|
||||||
|
|
||||||
|
// and a second copy so we don't need an extra mask or static initializer
|
||||||
|
23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123,
|
||||||
|
152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72,
|
||||||
|
175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240,
|
||||||
|
8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57,
|
||||||
|
225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233,
|
||||||
|
94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172,
|
||||||
|
165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243,
|
||||||
|
65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122,
|
||||||
|
26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76,
|
||||||
|
250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246,
|
||||||
|
132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3,
|
||||||
|
91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231,
|
||||||
|
38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221,
|
||||||
|
131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62,
|
||||||
|
27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135,
|
||||||
|
61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
static float stb__perlin_lerp(float a, float b, float t)
|
||||||
|
{
|
||||||
|
return a + (b-a) * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// different grad function from Perlin's, but easy to modify to match reference
|
||||||
|
static float stb__perlin_grad(int hash, float x, float y, float z)
|
||||||
|
{
|
||||||
|
static float basis[12][4] =
|
||||||
|
{
|
||||||
|
{ 1, 1, 0 },
|
||||||
|
{ -1, 1, 0 },
|
||||||
|
{ 1,-1, 0 },
|
||||||
|
{ -1,-1, 0 },
|
||||||
|
{ 1, 0, 1 },
|
||||||
|
{ -1, 0, 1 },
|
||||||
|
{ 1, 0,-1 },
|
||||||
|
{ -1, 0,-1 },
|
||||||
|
{ 0, 1, 1 },
|
||||||
|
{ 0,-1, 1 },
|
||||||
|
{ 0, 1,-1 },
|
||||||
|
{ 0,-1,-1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
// perlin's gradient has 12 cases so some get used 1/16th of the time
|
||||||
|
// and some 2/16ths. We reduce bias by changing those fractions
|
||||||
|
// to 5/16ths and 6/16ths, and the same 4 cases get the extra weight.
|
||||||
|
static unsigned char indices[64] =
|
||||||
|
{
|
||||||
|
0,1,2,3,4,5,6,7,8,9,10,11,
|
||||||
|
0,9,1,11,
|
||||||
|
0,1,2,3,4,5,6,7,8,9,10,11,
|
||||||
|
0,1,2,3,4,5,6,7,8,9,10,11,
|
||||||
|
0,1,2,3,4,5,6,7,8,9,10,11,
|
||||||
|
0,1,2,3,4,5,6,7,8,9,10,11,
|
||||||
|
};
|
||||||
|
|
||||||
|
// if you use reference permutation table, change 63 below to 15 to match reference
|
||||||
|
float *grad = basis[indices[hash & 63]];
|
||||||
|
return grad[0]*x + grad[1]*y + grad[2]*z;
|
||||||
|
}
|
||||||
|
|
||||||
|
float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap)
|
||||||
|
{
|
||||||
|
float u,v,w;
|
||||||
|
float n000,n001,n010,n011,n100,n101,n110,n111;
|
||||||
|
float n00,n01,n10,n11;
|
||||||
|
float n0,n1;
|
||||||
|
|
||||||
|
unsigned int x_mask = (x_wrap-1) & 255;
|
||||||
|
unsigned int y_mask = (y_wrap-1) & 255;
|
||||||
|
unsigned int z_mask = (z_wrap-1) & 255;
|
||||||
|
int px = (int) floor(x);
|
||||||
|
int py = (int) floor(y);
|
||||||
|
int pz = (int) floor(z);
|
||||||
|
int x0 = px & x_mask, x1 = (px+1) & x_mask;
|
||||||
|
int y0 = py & y_mask, y1 = (py+1) & y_mask;
|
||||||
|
int z0 = pz & z_mask, z1 = (pz+1) & z_mask;
|
||||||
|
int r0,r1, r00,r01,r10,r11;
|
||||||
|
|
||||||
|
#define stb__perlin_ease(a) (((a*6-15)*a + 10) * a * a * a)
|
||||||
|
|
||||||
|
x -= px; u = stb__perlin_ease(x);
|
||||||
|
y -= py; v = stb__perlin_ease(y);
|
||||||
|
z -= pz; w = stb__perlin_ease(z);
|
||||||
|
|
||||||
|
r0 = stb__perlin_randtab[x0];
|
||||||
|
r1 = stb__perlin_randtab[x1];
|
||||||
|
|
||||||
|
r00 = stb__perlin_randtab[r0+y0];
|
||||||
|
r01 = stb__perlin_randtab[r0+y1];
|
||||||
|
r10 = stb__perlin_randtab[r1+y0];
|
||||||
|
r11 = stb__perlin_randtab[r1+y1];
|
||||||
|
|
||||||
|
n000 = stb__perlin_grad(stb__perlin_randtab[r00+z0], x , y , z );
|
||||||
|
n001 = stb__perlin_grad(stb__perlin_randtab[r00+z1], x , y , z-1 );
|
||||||
|
n010 = stb__perlin_grad(stb__perlin_randtab[r01+z0], x , y-1, z );
|
||||||
|
n011 = stb__perlin_grad(stb__perlin_randtab[r01+z1], x , y-1, z-1 );
|
||||||
|
n100 = stb__perlin_grad(stb__perlin_randtab[r10+z0], x-1, y , z );
|
||||||
|
n101 = stb__perlin_grad(stb__perlin_randtab[r10+z1], x-1, y , z-1 );
|
||||||
|
n110 = stb__perlin_grad(stb__perlin_randtab[r11+z0], x-1, y-1, z );
|
||||||
|
n111 = stb__perlin_grad(stb__perlin_randtab[r11+z1], x-1, y-1, z-1 );
|
||||||
|
|
||||||
|
n00 = stb__perlin_lerp(n000,n001,w);
|
||||||
|
n01 = stb__perlin_lerp(n010,n011,w);
|
||||||
|
n10 = stb__perlin_lerp(n100,n101,w);
|
||||||
|
n11 = stb__perlin_lerp(n110,n111,w);
|
||||||
|
|
||||||
|
n0 = stb__perlin_lerp(n00,n01,v);
|
||||||
|
n1 = stb__perlin_lerp(n10,n11,v);
|
||||||
|
|
||||||
|
return stb__perlin_lerp(n0,n1,u);
|
||||||
|
}
|
||||||
|
#endif // STB_PERLIN_IMPLEMENTATION
|
1247
stb_textedit.h
Normal file
1247
stb_textedit.h
Normal file
File diff suppressed because it is too large
Load Diff
5370
stb_vorbis.c
Normal file
5370
stb_vorbis.c
Normal file
File diff suppressed because it is too large
Load Diff
3403
tests/stb.c
Normal file
3403
tests/stb.c
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user