Compare commits

..

No commits in common. "master" and "weighted" have entirely different histories.

7 changed files with 488 additions and 1378 deletions

View file

@ -60,7 +60,7 @@ offsets are sorted in ascending distance from the center (the 0,0 point).
the order of equal distances is undefined. the order of equal distances is undefined.
this doesn't matter much in practice, this doesn't matter much in practice,
considering the algorithm handles non-circular considering the algorithm handles non-circular
neighborhoods just fine. neighborhoods just fine — more on that later.
consider the first 29 offsets, consider the first 29 offsets,
which is the default number of neighbors: which is the default number of neighbors:
@ -121,7 +121,7 @@ resynth includes the following header libraries:
* [rnd.h by Mattias Gustavsson][rnd] (public domain / MIT) * [rnd.h by Mattias Gustavsson][rnd] (public domain / MIT)
[stb]: https://github.com/nothings/stb [stb]: https://github.com/nothings/stb
[kyaa]: https://github.com/notwa/kyaa [kyaa]: https://gist.github.com/notwa/5d287d807ffe11bbb553462c9940445c#file-kyaa-md
[rnd]: https://github.com/mattiasgustavsson/libs [rnd]: https://github.com/mattiasgustavsson/libs
compile like compile like

121
kyaa.h
View file

@ -1,118 +1,113 @@
/* kyaa.h - macro hacks for handling main() arguments /* kyaa.h - macro hacks for handling main() arguments
This is free and unencumbered software released into the public domain. This is free and unencumbered software released into the public domain.
For more information, please refer to <http://unlicense.org> Refer to kyaa.md for documentation.
*/ */
#ifndef KYAA_ONCE #pragma once
#define KYAA_ONCE
/* set some sane defaults. */
#ifndef KYAA_OKAY #ifndef KYAA_OKAY
#define KYAA_OKAY 0 #define KYAA_OKAY 0
#endif #endif
#ifndef KYAA_FAIL #ifndef KYAA_ERROR
#define KYAA_FAIL 1 #define KYAA_ERROR 1
#endif #endif
#ifndef KYAA_OUT #ifndef KYAA_ITER
#define KYAA_OUT(...) printf(__VA_ARGS__) #define KYAA_ITER i
#endif #endif
#ifndef KYAA_ERR
#define KYAA_ERR(...) fprintf(stderr, __VA_ARGS__)
#endif
#define KYAA_IS_LONG(arg, name) \
(strncmp(arg, "--" name, strlen("--" name)) == 0 && \
(arg[strlen("--" name)] == '\0' || arg[strlen("--" name)] == '='))
#define KYAA_SETUP \ #define KYAA_SETUP \
/* sanity checks */ \
if (argc <= 0 || argv == NULL || argv[0] == NULL) { \ if (argc <= 0 || argv == NULL || argv[0] == NULL) { \
KYAA_ERR("malformed argc/argv\n"); \ fprintf(stderr, "You've met with a terrible fate.\n"); \
return KYAA_FAIL; \ return KYAA_ERROR; \
} \ } \
const char *kyaa_name = argv[0]; \ char *kyaa_name = argv[0]; \
bool kyaa_read_stdin = false; \ bool kyaa_read_stdin = false; \
char kyaa_char = '\0'; \ char kyaa_flag = '\0'; \
bool kyaa_parsing = true; \ bool kyaa_keep_parsing = true; \
bool kyaa_skip = false; \ bool kyaa_parse_next = false; \
#define KYAA_LOOP \ #define KYAA_LOOP \
KYAA_SETUP \ KYAA_SETUP \
for (int kyaa_iter = 1; kyaa_iter < argc; kyaa_iter++) \ for (int KYAA_ITER = 1; KYAA_ITER < argc; KYAA_ITER++) \
#define KYAA_BEGIN \ #define KYAA_BEGIN \
const char *kyaa_arg = argv[kyaa_iter]; \ char *kyaa_arg = argv[KYAA_ITER]; \
if (kyaa_parsing && (kyaa_skip || kyaa_arg[0] == '-')) { \ if (kyaa_keep_parsing && (kyaa_parse_next || kyaa_arg[0] == '-')) { \
if (!kyaa_skip && kyaa_arg[1] == '-' && kyaa_arg[2] == '\0') { \ if (!kyaa_parse_next && kyaa_arg[1] == '-' && kyaa_arg[2] == '\0') { \
kyaa_parsing = false; \ kyaa_keep_parsing = false; \
continue; \ continue; \
} \ } \
if (!kyaa_skip && kyaa_arg[1] == '\0') kyaa_read_stdin = true; \ if (!kyaa_parse_next && kyaa_arg[1] == '\0') { \
else { \ kyaa_read_stdin = true; \
/* case: kyaa_skip: kyaa_arg is at least 1 char long. */ \ } else { \
/* case: !kyaa_skip: kyaa_arg is at least 3 chars long. */ \ /* case: kyaa_parse_next: arg is at least 1 char initialized. */ \
const char *kyaa_etc = kyaa_skip ? kyaa_arg : kyaa_arg + 2; \ /* case: !kyaa_parse_next: arg is at least 3 chars initialized. */ \
const char *kyaa_equals = kyaa_skip ? NULL : strchr(kyaa_etc, '='); \ char *kyaa_etc = kyaa_parse_next ? kyaa_arg : kyaa_arg + 2; \
bool kyaa_no_more = false; \
bool kyaa_helping = false; \ bool kyaa_helping = false; \
bool kyaa_any = false; \ bool kyaa_any = false; \
if (!kyaa_skip) { \ if (!kyaa_parse_next && kyaa_arg[1] != '-') { \
if (kyaa_arg[1] != '-') { \ kyaa_flag = kyaa_arg[1]; \
kyaa_char = kyaa_arg[1]; \ kyaa_no_more = kyaa_arg[2] == '\0'; \
if (kyaa_arg[2] == '\0') kyaa_etc = NULL; \ } \
} else if (kyaa_equals == NULL) kyaa_etc = NULL; \ if (kyaa_flag == 'h' || !strcmp(kyaa_arg, "--help")) { \
else kyaa_etc = kyaa_equals + 1; \ printf("usage:\n"); \
if (strcmp(kyaa_arg, "--help") == 0) kyaa_char = 'h'; \ kyaa_helping = true; \
if (kyaa_char == 'h') { \
KYAA_OUT("usage:\n"); \
kyaa_helping = true; \
} \
} \ } \
if (0) { \ if (0) { \
#define KYAA_END \ #define KYAA_END \
} \ } \
if (!kyaa_any && !kyaa_helping) { \ if (!kyaa_any && !kyaa_helping) { \
if (kyaa_char) KYAA_ERR("unknown flag: -%c\n", kyaa_char); \ if (kyaa_flag) { \
else KYAA_ERR("unknown flag: %s\n", kyaa_arg); \ fprintf(stderr, "unknown flag: -%c\n", kyaa_flag); \
return KYAA_FAIL; \ } else { \
fprintf(stderr, "unknown flag: %s\n", kyaa_arg); \
} \
return KYAA_ERROR; \
} \ } \
if (kyaa_helping) { \ if (kyaa_helping) { \
return KYAA_OKAY; \ return KYAA_OKAY; \
} \ } \
kyaa_skip = false; \ kyaa_parse_next = false; \
kyaa_char = '\0'; \ kyaa_flag = '\0'; \
continue; \ continue; \
} \ } \
} \ } \
#define KYAA_DESCRIBE(c, name, description) \ #define KYAA_DESCRIBE(c, name, description) \
KYAA_OUT(" -%c --%s\n%s\n", c, name, description) \ printf(" -%c --%s\n%s\n", c, name, description) \
#define KYAA_FLAG(c, name, description) \ #define KYAA_FLAG(c, name, description) \
} \ } \
if (kyaa_helping) KYAA_DESCRIBE(c, name, description); \ if (kyaa_helping) { \
else if (kyaa_char == c || KYAA_IS_LONG(kyaa_arg, name)) { \ KYAA_DESCRIBE(c, name, description); \
kyaa_char = c; \ } else if (kyaa_flag == c || !strcmp(kyaa_arg, "--"name)) { \
kyaa_flag = c; \
kyaa_any = true; \ kyaa_any = true; \
#define KYAA_FLAG_ARG(c, name, description) \ #define KYAA_FLAG_ARG(c, name, description) \
} \ } \
if (kyaa_helping) KYAA_DESCRIBE(c, name, description); \ if (kyaa_helping) { \
else if (kyaa_char == c || KYAA_IS_LONG(kyaa_arg, name)) { \ KYAA_DESCRIBE(c, name, description); \
if (kyaa_etc == NULL) { \ } else if (kyaa_flag == c || !strcmp(kyaa_arg, "--"name)) { \
kyaa_skip = true; \ if (kyaa_no_more || kyaa_flag != c) { \
if (kyaa_iter + 1 == argc || argv[kyaa_iter + 1] == NULL) { \ kyaa_parse_next = true; \
KYAA_ERR("expected an argument for --%s (-%c)\n", name, c); \ if (KYAA_ITER + 1 == argc || argv[KYAA_ITER + 1][0] == '\0') { \
return KYAA_FAIL; \ fprintf(stderr, "expected an argument for --%s (-%c)\n", name, c); \
return KYAA_ERROR; \
} \ } \
kyaa_char = c; \ kyaa_flag = c; \
continue; \ continue; \
} \ } \
kyaa_char = c; \ kyaa_flag = c; \
kyaa_any = true; \ kyaa_any = true; \
#define KYAA_HELP(description) \ #define KYAA_HELP(description) \
} \ } \
if (kyaa_helping) { \ if (kyaa_helping) { \
if (description != NULL) KYAA_OUT("%s\n", description); \ if (description != NULL) { \
printf("%s\n", description); \
} \
#endif /* KYAA_ONCE */

View file

@ -1,12 +1,9 @@
/* kyaa_extra.h - extensions to kyaa for parsing arguments. /* kyaa_extra.h - extensions to kyaa for parsing arguments.
This is free and unencumbered software released into the public domain. This is free and unencumbered software released into the public domain.
For more information, please refer to <http://unlicense.org> Refer to kyaa.md for documentation.
*/ */
#ifndef KYAA_EXTRA static char *kyaa_skip_spaces(char *str) {
#define KYAA_EXTRA
static const char *kyaa_skip_spaces(const char *str) {
/* iterates str to first non-space character according to the C locale */ /* iterates str to first non-space character according to the C locale */
while (*str != '\0') { while (*str != '\0') {
switch (*str) { switch (*str) {
@ -22,8 +19,8 @@ static const char *kyaa_skip_spaces(const char *str) {
return str; return str;
} }
static const char *kyaa__base_2(const char **p_str, long *p_out) { static const char *kyaa__base_2(char **p_str, long *p_out) {
const char *str = *p_str; char *str = *p_str;
long out = *p_out; long out = *p_out;
for (char c; (c = *str) != '\0'; str++) { for (char c; (c = *str) != '\0'; str++) {
switch (c) { switch (c) {
@ -50,8 +47,8 @@ exit:
return NULL; return NULL;
} }
static const char *kyaa__base_8(const char **p_str, long *p_out) { static const char *kyaa__base_8(char **p_str, long *p_out) {
const char *str = *p_str; char *str = *p_str;
long out = *p_out; long out = *p_out;
for (char c; (c = *str) != '\0'; str++) { for (char c; (c = *str) != '\0'; str++) {
switch (c) { switch (c) {
@ -78,8 +75,8 @@ exit:
return NULL; return NULL;
} }
static const char *kyaa__base_10(const char **p_str, long *p_out) { static const char *kyaa__base_10(char **p_str, long *p_out) {
const char *str = *p_str; char *str = *p_str;
long out = *p_out; long out = *p_out;
for (char c; (c = *str) != '\0'; str++) { for (char c; (c = *str) != '\0'; str++) {
switch (c) { switch (c) {
@ -105,8 +102,8 @@ exit:
return NULL; return NULL;
} }
static const char *kyaa__base_16(const char **p_str, long *p_out) { static const char *kyaa__base_16(char **p_str, long *p_out) {
const char *str = *p_str; char *str = *p_str;
long out = *p_out; long out = *p_out;
for (char c; (c = *str) != '\0'; str++) { for (char c; (c = *str) != '\0'; str++) {
switch (c) { switch (c) {
@ -146,7 +143,7 @@ exit:
return NULL; return NULL;
} }
static const char *kyaa_str_to_long(const char *str, long *p_out) { static const char *kyaa_str_to_long(char *str, long *p_out) {
/* returns error message or NULL */ /* returns error message or NULL */
long out = 0; long out = 0;
int base = 10; int base = 10;
@ -205,11 +202,10 @@ static const char *kyaa_str_to_long(const char *str, long *p_out) {
#define KYAA_FLAG_LONG(c, name, description) \ #define KYAA_FLAG_LONG(c, name, description) \
KYAA_FLAG_ARG(c, name, description) \ KYAA_FLAG_ARG(c, name, description) \
long kyaa_long_value; \ long kyaa_flag_arg; \
const char *err = kyaa_str_to_long(kyaa_etc, &kyaa_long_value); \ const char *err = kyaa_str_to_long(kyaa_etc, &kyaa_flag_arg); \
if (err) { \ if (err) { \
KYAA_ERR("%s\n", err); \ fprintf(stderr, "%s\n", err); \
return KYAA_FAIL; \ return KYAA_ERROR; \
} \ } \
#endif /* KYAA_EXTRA */

View file

@ -146,6 +146,7 @@ typedef struct {
double autism; double autism;
int neighbors, tries; int neighbors, tries;
int magic; int magic;
bool weighted;
} Parameters; } Parameters;
INLINE bool wrap_or_clip(const Parameters parameters, const Image image, INLINE bool wrap_or_clip(const Parameters parameters, const Image image,
@ -232,7 +233,14 @@ static void make_offset_list(Resynth_state *s) {
sizeof(Coord), coord_compare); sizeof(Coord), coord_compare);
} }
INLINE void try_point(Resynth_state *s, const Coord point) { INLINE int weight_at(const Coord point) {
if (point.x == 0 && point.y == 0) return 360;
// this is stable and accurate up to -N 37 (-R 9)
// 520 might work in place of 360 and provide more accuracy w/o overflows.
return 360 / (point.x * point.x + point.y * point.y);
}
INLINE void try_point(Resynth_state *s, const Coord point, bool weighted) {
// consider a candidate pixel for the best-fit by considering its neighbors. // consider a candidate pixel for the best-fit by considering its neighbors.
int sum = 0; int sum = 0;
@ -252,6 +260,7 @@ INLINE void try_point(Resynth_state *s, const Coord point) {
} }
} }
if (weighted) diff *= weight_at(s->neighbors[i]);
#ifdef NDEBUG #ifdef NDEBUG
sum += diff; sum += diff;
#else #else
@ -270,6 +279,7 @@ INLINE void try_point(Resynth_state *s, const Coord point) {
} }
INLINE void resynth__init(Resynth_state *s, Parameters parameters) { INLINE void resynth__init(Resynth_state *s, Parameters parameters) {
// "resynthesize" an output image from a given input image.
sb_freeset(s->data_points); sb_freeset(s->data_points);
sb_freeset(s->corpus_points); sb_freeset(s->corpus_points);
sb_freeset(s->sorted_offsets); sb_freeset(s->sorted_offsets);
@ -355,7 +365,6 @@ INLINE void resynth__init(Resynth_state *s, Parameters parameters) {
} }
static void resynth(Resynth_state *s, Parameters parameters) { static void resynth(Resynth_state *s, Parameters parameters) {
// "resynthesize" an output image from a given input image.
resynth__init(s, parameters); resynth__init(s, parameters);
for (int i = sb_count(s->data_points) - 1; i >= 0; i--) { for (int i = sb_count(s->data_points) - 1; i >= 0; i--) {
@ -399,7 +408,7 @@ static void resynth(Resynth_state *s, Parameters parameters) {
// skip computing differences of points // skip computing differences of points
// we've already done this iteration. not mandatory. // we've already done this iteration. not mandatory.
if (*image_atc(s->tried, point) == i) continue; if (*image_atc(s->tried, point) == i) continue;
try_point(s, point); try_point(s, point, parameters.weighted);
*image_atc(s->tried, point) = i; *image_atc(s->tried, point) = i;
} }
} }
@ -409,7 +418,7 @@ static void resynth(Resynth_state *s, Parameters parameters) {
// after that, this step is optional. it can improve subjective quality. // after that, this step is optional. it can improve subjective quality.
for (int j = 0; j < parameters.tries && s->best != 0; j++) { for (int j = 0; j < parameters.tries && s->best != 0; j++) {
int random = rnd_pcg_range(&pcg, 0, sb_count(s->corpus_points) - 1); int random = rnd_pcg_range(&pcg, 0, sb_count(s->corpus_points) - 1);
try_point(s, s->corpus_points[random]); try_point(s, s->corpus_points[random], parameters.weighted);
} }
// finally, copy the best pixel to the output image. // finally, copy the best pixel to the output image.
@ -466,6 +475,7 @@ int main(int argc, char *argv[]) {
Parameters parameters = {0}; Parameters parameters = {0};
parameters.v_tile = true; parameters.v_tile = true;
parameters.h_tile = true; parameters.h_tile = true;
parameters.weighted = false; // our extension
// blah = our default; // original resynthesizer default // blah = our default; // original resynthesizer default
parameters.magic = 192; // 192 (3/4) parameters.magic = 192; // 192 (3/4)
parameters.autism = 32. / 256.; // 30. / 256. parameters.autism = 32. / 256.; // 30. / 256.
@ -484,39 +494,48 @@ int main(int argc, char *argv[]) {
KYAA_FLAG_LONG('a', "autism", KYAA_FLAG_LONG('a', "autism",
" sensitivity to outliers\n" " sensitivity to outliers\n"
" range: [0,256]; default: 32") " range: [0,256]; default: 32")
parameters.autism = (double)(kyaa_long_value) / 256.; parameters.autism = (double)(kyaa_flag_arg) / 256.;
KYAA_FLAG_LONG('N', "neighbors", KYAA_FLAG_LONG('N', "neighbors",
" points to use when sampling\n" " points to use when sampling\n"
" range: [0,1024]; default: 29") " range: [0,1024]; default: 29")
parameters.neighbors = kyaa_long_value; parameters.neighbors = kyaa_flag_arg;
KYAA_FLAG_LONG('R', "circle-radius", KYAA_FLAG_LONG('R', "circle-radius",
" circle neighborhood radius\n" " circle neighborhood radius\n"
" range: [1,128]; default: [n/a]") " range: [1,128]; default: [n/a]")
int radius = kyaa_long_value; int radius = kyaa_flag_arg;
radius = CLAMP(radius, 1, (int)(LEN(disc00))); radius = CLAMP(radius, 1, (int)(LEN(disc00)));
parameters.neighbors = disc00[radius - 1]; parameters.neighbors = disc00[radius - 1];
KYAA_FLAG_LONG('M', "tries", KYAA_FLAG_LONG('M', "tries",
" random points added to candidates\n" " random points added to candidates\n"
" range: [0,65536]; default: 192") " range: [0,65536]; default: 192")
parameters.tries = kyaa_long_value; parameters.tries = kyaa_flag_arg;
KYAA_FLAG_LONG('m', "magic", KYAA_FLAG_LONG('m', "magic",
" magic constant, affects iterations\n" " magic constant, affects iterations\n"
" range: [0,255]; default: 192") " range: [0,255]; default: 192")
parameters.magic = kyaa_long_value; parameters.magic = kyaa_flag_arg;
KYAA_FLAG_LONG('s', "scale", KYAA_FLAG_LONG('s', "scale",
" output size multiplier; negative values set width and height\n" " output size multiplier; negative values set width and height\n"
" range: [-8192,32]; default: 1") " range: [-8192,32]; default: 1")
scale = kyaa_long_value; scale = kyaa_flag_arg;
KYAA_FLAG_LONG('S', "seed", KYAA_FLAG_LONG('S', "seed",
" initial RNG value\n" " initial RNG value\n"
" default: 0 [time(0)]") " default: 0 [time(0)]")
seed = (unsigned long) kyaa_long_value; seed = (unsigned long) kyaa_flag_arg;
KYAA_FLAG('w', "weighted",
" enables something like laplace convolution but not really\n"
" default: off")
parameters.weighted = true;
KYAA_FLAG('W', "unweighted",
" disables --weighted")
parameters.weighted = false;
KYAA_HELP(" {files...}\n" KYAA_HELP(" {files...}\n"
" image files to open, resynthesize, and save as {filename}.resynth.png\n" " image files to open, resynthesize, and save as {filename}.resynth.png\n"

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
/* stb_image_write - v1.13 - public domain - http://nothings.org/stb/stb_image_write.h /* stb_image_write - v1.05 - public domain - http://nothings.org/stb/stb_image_write.h
writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010-2015
no warranty implied; use at your own risk no warranty implied; use at your own risk
Before #including, Before #including,
@ -12,64 +12,41 @@
ABOUT: ABOUT:
This header file is a library for writing images to C stdio or a callback. 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 The PNG output is not optimal; it is 20-50% larger than the file
written by a decent optimizing implementation; though providing a custom written by a decent optimizing implementation. This library is designed
zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. for source code compactness and simplicity, not optimal image file size
This library is designed for source code compactness and simplicity, or run-time performance.
not optimal image file size or run-time performance.
BUILDING: BUILDING:
You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h.
You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace
malloc,realloc,free. malloc,realloc,free.
You can #define STBIW_MEMMOVE() to replace memmove() You can define STBIW_MEMMOVE() to replace memmove()
You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function
for PNG compression (instead of the builtin one), it must have the following signature:
unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality);
The returned data will be freed with STBIW_FREE() (free() by default),
so it must be heap allocated with STBIW_MALLOC() (malloc() by default),
UNICODE:
If compiling for Windows and you wish to use Unicode filenames, compile
with
#define STBIW_WINDOWS_UTF8
and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert
Windows wchar_t filenames to utf8.
USAGE: USAGE:
There are five functions, one for each image file format: There are four 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_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_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); int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality);
int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);
void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically There are also four equivalent functions that use an arbitrary write function. You are
There are also five equivalent functions that use an arbitrary write function. You are
expected to open/close your file-equivalent before and after calling these: expected to open/close your file-equivalent before and after calling these:
int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes);
int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data);
int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality);
where the callback is: where the callback is:
void stbi_write_func(void *context, void *data, int size); void stbi_write_func(void *context, void *data, int size);
You can configure it with these global variables:
int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE
int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression
int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode
You can define STBI_WRITE_NO_STDIO to disable the file variant of these You can define STBI_WRITE_NO_STDIO to disable the file variant of these
functions, so the library will not use stdio.h at all. However, this will functions, so the library will not use stdio.h at all. However, this will
also disable HDR writing, because it requires stdio for formatted output. also disable HDR writing, because it requires stdio for formatted output.
@ -96,33 +73,27 @@ USAGE:
writer, both because it is in BGR order and because it may have padding writer, both because it is in BGR order and because it may have padding
at the end of the line.) at the end of the line.)
PNG allows you to set the deflate compression level by setting the global
variable 'stbi_write_png_compression_level' (it defaults to 8).
HDR expects linear float data. Since the format is always 32-bit rgb(e) HDR expects linear float data. Since the format is always 32-bit rgb(e)
data, alpha (if provided) is discarded, and for monochrome data it is data, alpha (if provided) is discarded, and for monochrome data it is
replicated across all three channels. replicated across all three channels.
TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed
data, set the global variable 'stbi_write_tga_with_rle' to 0. data, set the global variable 'stbi_write_tga_with_rle' to 0.
JPEG does ignore alpha channels in input data; quality is between 1 and 100.
Higher quality looks better but results in a bigger image.
JPEG baseline (no JPEG progressive).
CREDITS: CREDITS:
PNG/BMP/TGA
Sean Barrett - PNG/BMP/TGA Sean Barrett
Baldur Karlsson - HDR HDR
Jean-Sebastien Guay - TGA monochrome Baldur Karlsson
Tim Kelsey - misc enhancements TGA monochrome:
Alan Hickman - TGA RLE Jean-Sebastien Guay
Emmanuel Julien - initial file IO callback implementation misc enhancements:
Jon Olick - original jo_jpeg.cpp code Tim Kelsey
Daniel Gibson - integrate JPEG, allow external zlib TGA RLE
Aarni Koskela - allow choosing PNG filter Alan Hickman
initial file IO callback implementation
Emmanuel Julien
bugfixes: bugfixes:
github:Chribba github:Chribba
Guillaume Chereau Guillaume Chereau
@ -134,13 +105,7 @@ CREDITS:
Thatcher Ulrich Thatcher Ulrich
github:poppolopoppo github:poppolopoppo
Patrick Boettcher Patrick Boettcher
github:xeekworx
Cap Petschulat
Simon Rodriguez
Ivan Tikhonov
github:ignotion
Adam Schackart
LICENSE LICENSE
See end of file for license information. See end of file for license information.
@ -150,25 +115,15 @@ LICENSE
#ifndef INCLUDE_STB_IMAGE_WRITE_H #ifndef INCLUDE_STB_IMAGE_WRITE_H
#define INCLUDE_STB_IMAGE_WRITE_H #define INCLUDE_STB_IMAGE_WRITE_H
#include <stdlib.h>
// if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline'
#ifndef STBIWDEF
#ifdef STB_IMAGE_WRITE_STATIC
#define STBIWDEF static
#else
#ifdef __cplusplus #ifdef __cplusplus
#define STBIWDEF extern "C" extern "C" {
#else
#define STBIWDEF extern
#endif
#endif
#endif #endif
#ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations #ifdef STB_IMAGE_WRITE_STATIC
#define STBIWDEF static
#else
#define STBIWDEF extern
extern int stbi_write_tga_with_rle; extern int stbi_write_tga_with_rle;
extern int stbi_write_png_compression_level;
extern int stbi_write_force_png_filter;
#endif #endif
#ifndef STBI_WRITE_NO_STDIO #ifndef STBI_WRITE_NO_STDIO
@ -176,11 +131,6 @@ STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const
STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);
STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality);
#ifdef STBI_WINDOWS_UTF8
STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input);
#endif
#endif #endif
typedef void stbi_write_func(void *context, void *data, int size); typedef void stbi_write_func(void *context, void *data, int size);
@ -189,9 +139,10 @@ STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w,
STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data);
STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality);
STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); #ifdef __cplusplus
}
#endif
#endif//INCLUDE_STB_IMAGE_WRITE_H #endif//INCLUDE_STB_IMAGE_WRITE_H
@ -246,23 +197,6 @@ STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean);
#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) #define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff)
#ifdef STB_IMAGE_WRITE_STATIC
static int stbi__flip_vertically_on_write=0;
static int stbi_write_png_compression_level = 8;
static int stbi_write_tga_with_rle = 1;
static int stbi_write_force_png_filter = -1;
#else
int stbi_write_png_compression_level = 8;
int stbi__flip_vertically_on_write=0;
int stbi_write_tga_with_rle = 1;
int stbi_write_force_png_filter = -1;
#endif
STBIWDEF void stbi_flip_vertically_on_write(int flag)
{
stbi__flip_vertically_on_write = flag;
}
typedef struct typedef struct
{ {
stbi_write_func *func; stbi_write_func *func;
@ -283,52 +217,9 @@ static void stbi__stdio_write(void *context, void *data, int size)
fwrite(data,1,size,(FILE*) context); fwrite(data,1,size,(FILE*) context);
} }
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
#ifdef __cplusplus
#define STBIW_EXTERN extern "C"
#else
#define STBIW_EXTERN extern
#endif
STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide);
STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default);
STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input)
{
return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL);
}
#endif
static FILE *stbiw__fopen(char const *filename, char const *mode)
{
FILE *f;
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
wchar_t wMode[64];
wchar_t wFilename[1024];
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)))
return 0;
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)))
return 0;
#if _MSC_VER >= 1400
if (0 != _wfopen_s(&f, wFilename, wMode))
f = 0;
#else
f = _wfopen(wFilename, wMode);
#endif
#elif defined(_MSC_VER) && _MSC_VER >= 1400
if (0 != fopen_s(&f, filename, mode))
f=0;
#else
f = fopen(filename, mode);
#endif
return f;
}
static int stbi__start_write_file(stbi__write_context *s, const char *filename) static int stbi__start_write_file(stbi__write_context *s, const char *filename)
{ {
FILE *f = stbiw__fopen(filename, "wb"); FILE *f = fopen(filename, "wb");
stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f);
return f != NULL; return f != NULL;
} }
@ -343,6 +234,12 @@ static void stbi__end_write_file(stbi__write_context *s)
typedef unsigned int stbiw_uint32; typedef unsigned int stbiw_uint32;
typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1];
#ifdef STB_IMAGE_WRITE_STATIC
static int stbi_write_tga_with_rle = 1;
#else
int stbi_write_tga_with_rle = 1;
#endif
static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v)
{ {
while (*fmt) { while (*fmt) {
@ -380,15 +277,10 @@ static void stbiw__writef(stbi__write_context *s, const char *fmt, ...)
va_end(v); va_end(v);
} }
static void stbiw__putc(stbi__write_context *s, unsigned char c)
{
s->func(s->context, &c, 1);
}
static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c)
{ {
unsigned char arr[3]; unsigned char arr[3];
arr[0] = a; arr[1] = b; arr[2] = c; arr[0] = a, arr[1] = b, arr[2] = c;
s->func(s->context, arr, 3); s->func(s->context, arr, 3);
} }
@ -433,14 +325,10 @@ static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, i
if (y <= 0) if (y <= 0)
return; return;
if (stbi__flip_vertically_on_write) if (vdir < 0)
vdir *= -1; j_end = -1, j = y-1;
else
if (vdir < 0) { j_end = y, j = 0;
j_end = -1; j = y-1;
} else {
j_end = y; j = 0;
}
for (; j != j_end; j += vdir) { for (; j != j_end; j += vdir) {
for (i=0; i < x; ++i) { for (i=0; i < x; ++i) {
@ -508,21 +396,11 @@ static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, v
"111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8);
} else { } else {
int i,j,k; int i,j,k;
int jend, jdir;
stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8);
if (stbi__flip_vertically_on_write) { for (j = y - 1; j >= 0; --j) {
j = 0; unsigned char *row = (unsigned char *) data + j * x * comp;
jend = y;
jdir = 1;
} else {
j = y-1;
jend = -1;
jdir = -1;
}
for (; j != jend; j += jdir) {
unsigned char *row = (unsigned char *) data + j * x * comp;
int len; int len;
for (i = 0; i < x; i += len) { for (i = 0; i < x; i += len) {
@ -572,7 +450,7 @@ static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, v
return 1; return 1;
} }
STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data)
{ {
stbi__write_context s; stbi__write_context s;
stbi__start_write_callbacks(&s, func, context); stbi__start_write_callbacks(&s, func, context);
@ -580,7 +458,7 @@ STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x,
} }
#ifndef STBI_WRITE_NO_STDIO #ifndef STBI_WRITE_NO_STDIO
STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data)
{ {
stbi__write_context s; stbi__write_context s;
if (stbi__start_write_file(&s,filename)) { if (stbi__start_write_file(&s,filename)) {
@ -598,7 +476,7 @@ STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const
#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) #define stbiw__max(a, b) ((a) > (b) ? (a) : (b))
static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear)
{ {
int exponent; int exponent;
float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2]));
@ -615,7 +493,7 @@ static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear)
} }
} }
static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte)
{ {
unsigned char lengthbyte = STBIW_UCHAR(length+128); unsigned char lengthbyte = STBIW_UCHAR(length+128);
STBIW_ASSERT(length+128 <= 255); STBIW_ASSERT(length+128 <= 255);
@ -623,7 +501,7 @@ static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned c
s->func(s->context, &databyte, 1); s->func(s->context, &databyte, 1);
} }
static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data)
{ {
unsigned char lengthbyte = STBIW_UCHAR(length); unsigned char lengthbyte = STBIW_UCHAR(length);
STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code
@ -631,7 +509,7 @@ static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned
s->func(s->context, data, length); s->func(s->context, data, length);
} }
static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline)
{ {
unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; unsigned char scanlineheader[4] = { 2, 2, 0, 0 };
unsigned char rgbe[4]; unsigned char rgbe[4];
@ -732,21 +610,17 @@ static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, f
char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n";
s->func(s->context, header, sizeof(header)-1); s->func(s->context, header, sizeof(header)-1);
#ifdef __STDC_WANT_SECURE_LIB__
len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
#else
len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
#endif
s->func(s->context, buffer, len); s->func(s->context, buffer, len);
for(i=0; i < y; i++) for(i=0; i < y; i++)
stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)); stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*i*x);
STBIW_FREE(scratch); STBIW_FREE(scratch);
return 1; return 1;
} }
} }
STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data)
{ {
stbi__write_context s; stbi__write_context s;
stbi__start_write_callbacks(&s, func, context); stbi__start_write_callbacks(&s, func, context);
@ -754,7 +628,7 @@ STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x,
} }
#ifndef STBI_WRITE_NO_STDIO #ifndef STBI_WRITE_NO_STDIO
STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data)
{ {
stbi__write_context s; stbi__write_context s;
if (stbi__start_write_file(&s,filename)) { if (stbi__start_write_file(&s,filename)) {
@ -772,7 +646,6 @@ STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const
// PNG writer // PNG writer
// //
#ifndef STBIW_ZLIB_COMPRESS
// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size()
#define stbiw__sbraw(a) ((int *) (a) - 2) #define stbiw__sbraw(a) ((int *) (a) - 2)
#define stbiw__sbm(a) stbiw__sbraw(a)[0] #define stbiw__sbm(a) stbiw__sbraw(a)[0]
@ -853,14 +726,8 @@ static unsigned int stbiw__zhash(unsigned char *data)
#define stbiw__ZHASH 16384 #define stbiw__ZHASH 16384
#endif // STBIW_ZLIB_COMPRESS unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality)
STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality)
{ {
#ifdef STBIW_ZLIB_COMPRESS
// user provided a zlib compress implementation, use that
return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality);
#else // use builtin
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 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 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 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 };
@ -869,8 +736,6 @@ STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, i
int i,j, bitcount=0; int i,j, bitcount=0;
unsigned char *out = NULL; unsigned char *out = NULL;
unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**));
if (hash_table == NULL)
return NULL;
if (quality < 5) quality = 5; if (quality < 5) quality = 5;
stbiw__sbpush(out, 0x78); // DEFLATE 32K window stbiw__sbpush(out, 0x78); // DEFLATE 32K window
@ -891,7 +756,7 @@ STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, i
for (j=0; j < n; ++j) { for (j=0; j < n; ++j) {
if (hlist[j]-data > i-32768) { // if entry lies within window if (hlist[j]-data > i-32768) { // if entry lies within window
int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i);
if (d >= best) { best=d; bestloc=hlist[j]; } if (d >= best) best=d,bestloc=hlist[j];
} }
} }
// when hash table entry is too long, delete half the entries // when hash table entry is too long, delete half the entries
@ -950,8 +815,8 @@ STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, i
int blocklen = (int) (data_len % 5552); int blocklen = (int) (data_len % 5552);
j=0; j=0;
while (j < data_len) { while (j < data_len) {
for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; } for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1;
s1 %= 65521; s2 %= 65521; s1 %= 65521, s2 %= 65521;
j += blocklen; j += blocklen;
blocklen = 5552; blocklen = 5552;
} }
@ -964,14 +829,10 @@ STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, i
// make returned pointer freeable // make returned pointer freeable
STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len);
return (unsigned char *) stbiw__sbraw(out); return (unsigned char *) stbiw__sbraw(out);
#endif // STBIW_ZLIB_COMPRESS
} }
static unsigned int stbiw__crc32(unsigned char *buffer, int len) static unsigned int stbiw__crc32(unsigned char *buffer, int len)
{ {
#ifdef STBIW_CRC32
return STBIW_CRC32(buffer, len);
#else
static unsigned int crc_table[256] = static unsigned int crc_table[256] =
{ {
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
@ -1013,7 +874,6 @@ static unsigned int stbiw__crc32(unsigned char *buffer, int len)
for (i=0; i < len; ++i) for (i=0; i < len; ++i)
crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)];
return ~crc; return ~crc;
#endif
} }
#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) #define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4)
@ -1035,91 +895,61 @@ static unsigned char stbiw__paeth(int a, int b, int c)
} }
// @OPTIMIZE: provide an option that always forces left-predict or paeth predict // @OPTIMIZE: provide an option that always forces left-predict or paeth predict
static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len)
{ {
static int mapping[] = { 0,1,2,3,4 };
static int firstmap[] = { 0,1,0,5,6 };
int *mymap = (y != 0) ? mapping : firstmap;
int i;
int type = mymap[filter_type];
unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y);
int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes;
if (type==0) {
memcpy(line_buffer, z, width*n);
return;
}
// first loop isn't optimized since it's just one pixel
for (i = 0; i < n; ++i) {
switch (type) {
case 1: line_buffer[i] = z[i]; break;
case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break;
case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break;
case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break;
case 5: line_buffer[i] = z[i]; break;
case 6: line_buffer[i] = z[i]; break;
}
}
switch (type) {
case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break;
case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break;
case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break;
case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break;
case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break;
case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break;
}
}
STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len)
{
int force_filter = stbi_write_force_png_filter;
int ctype[5] = { -1, 0, 4, 2, 6 }; int ctype[5] = { -1, 0, 4, 2, 6 };
unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; unsigned char sig[8] = { 137,80,78,71,13,10,26,10 };
unsigned char *out,*o, *filt, *zlib; unsigned char *out,*o, *filt, *zlib;
signed char *line_buffer; signed char *line_buffer;
int j,zlen; int i,j,k,p,zlen;
if (stride_bytes == 0) if (stride_bytes == 0)
stride_bytes = x * n; stride_bytes = x * n;
if (force_filter >= 5) {
force_filter = -1;
}
filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0;
line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; }
for (j=0; j < y; ++j) { for (j=0; j < y; ++j) {
int filter_type; static int mapping[] = { 0,1,2,3,4 };
if (force_filter > -1) { static int firstmap[] = { 0,1,0,5,6 };
filter_type = force_filter; int *mymap = (j != 0) ? mapping : firstmap;
stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); int best = 0, bestval = 0x7fffffff;
} else { // Estimate the best filter by running through all of them: for (p=0; p < 2; ++p) {
int best_filter = 0, best_filter_val = 0x7fffffff, est, i; for (k= p?best:0; k < 5; ++k) { // @TODO: clarity: rewrite this to go 0..5, and 'continue' the unwanted ones during 2nd pass
for (filter_type = 0; filter_type < 5; filter_type++) { int type = mymap[k],est=0;
stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); unsigned char *z = pixels + stride_bytes*j;
for (i=0; i < n; ++i)
// Estimate the entropy of the line using this filter; the less, the better. switch (type) {
est = 0; case 0: line_buffer[i] = z[i]; break;
for (i = 0; i < x*n; ++i) { 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] - stbiw__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] - stbiw__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] - stbiw__paeth(z[i-n], 0,0); break;
}
}
if (p) break;
for (i=0; i < x*n; ++i)
est += abs((signed char) line_buffer[i]); est += abs((signed char) line_buffer[i]);
} if (est < bestval) { bestval = est; best = k; }
if (est < best_filter_val) {
best_filter_val = est;
best_filter = filter_type;
}
}
if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it
stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer);
filter_type = best_filter;
} }
} }
// when we get here, filter_type contains the filter type, and line_buffer contains the data // when we get here, best contains the filter type, and line_buffer contains the data
filt[j*(x*n+1)] = (unsigned char) filter_type; filt[j*(x*n+1)] = (unsigned char) best;
STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n);
} }
STBIW_FREE(line_buffer); STBIW_FREE(line_buffer);
zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory
STBIW_FREE(filt); STBIW_FREE(filt);
if (!zlib) return 0; if (!zlib) return 0;
@ -1162,10 +992,9 @@ STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const
{ {
FILE *f; FILE *f;
int len; int len;
unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len);
if (png == NULL) return 0; if (png == NULL) return 0;
f = fopen(filename, "wb");
f = stbiw__fopen(filename, "wb");
if (!f) { STBIW_FREE(png); return 0; } if (!f) { STBIW_FREE(png); return 0; }
fwrite(png, 1, len, f); fwrite(png, 1, len, f);
fclose(f); fclose(f);
@ -1177,370 +1006,16 @@ STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const
STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes)
{ {
int len; int len;
unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len);
if (png == NULL) return 0; if (png == NULL) return 0;
func(context, png, len); func(context, png, len);
STBIW_FREE(png); STBIW_FREE(png);
return 1; return 1;
} }
/* ***************************************************************************
*
* JPEG writer
*
* This is based on Jon Olick's jo_jpeg.cpp:
* public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html
*/
static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18,
24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 };
static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) {
int bitBuf = *bitBufP, bitCnt = *bitCntP;
bitCnt += bs[1];
bitBuf |= bs[0] << (24 - bitCnt);
while(bitCnt >= 8) {
unsigned char c = (bitBuf >> 16) & 255;
stbiw__putc(s, c);
if(c == 255) {
stbiw__putc(s, 0);
}
bitBuf <<= 8;
bitCnt -= 8;
}
*bitBufP = bitBuf;
*bitCntP = bitCnt;
}
static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) {
float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p;
float z1, z2, z3, z4, z5, z11, z13;
float tmp0 = d0 + d7;
float tmp7 = d0 - d7;
float tmp1 = d1 + d6;
float tmp6 = d1 - d6;
float tmp2 = d2 + d5;
float tmp5 = d2 - d5;
float tmp3 = d3 + d4;
float tmp4 = d3 - d4;
// Even part
float tmp10 = tmp0 + tmp3; // phase 2
float tmp13 = tmp0 - tmp3;
float tmp11 = tmp1 + tmp2;
float tmp12 = tmp1 - tmp2;
d0 = tmp10 + tmp11; // phase 3
d4 = tmp10 - tmp11;
z1 = (tmp12 + tmp13) * 0.707106781f; // c4
d2 = tmp13 + z1; // phase 5
d6 = tmp13 - z1;
// Odd part
tmp10 = tmp4 + tmp5; // phase 2
tmp11 = tmp5 + tmp6;
tmp12 = tmp6 + tmp7;
// The rotator is modified from fig 4-8 to avoid extra negations.
z5 = (tmp10 - tmp12) * 0.382683433f; // c6
z2 = tmp10 * 0.541196100f + z5; // c2-c6
z4 = tmp12 * 1.306562965f + z5; // c2+c6
z3 = tmp11 * 0.707106781f; // c4
z11 = tmp7 + z3; // phase 5
z13 = tmp7 - z3;
*d5p = z13 + z2; // phase 6
*d3p = z13 - z2;
*d1p = z11 + z4;
*d7p = z11 - z4;
*d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6;
}
static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) {
int tmp1 = val < 0 ? -val : val;
val = val < 0 ? val-1 : val;
bits[1] = 1;
while(tmp1 >>= 1) {
++bits[1];
}
bits[0] = val & ((1<<bits[1])-1);
}
static int stbiw__jpg_processDU(stbi__write_context *s, int *bitBuf, int *bitCnt, float *CDU, float *fdtbl, int DC, const unsigned short HTDC[256][2], const unsigned short HTAC[256][2]) {
const unsigned short EOB[2] = { HTAC[0x00][0], HTAC[0x00][1] };
const unsigned short M16zeroes[2] = { HTAC[0xF0][0], HTAC[0xF0][1] };
int dataOff, i, diff, end0pos;
int DU[64];
// DCT rows
for(dataOff=0; dataOff<64; dataOff+=8) {
stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+1], &CDU[dataOff+2], &CDU[dataOff+3], &CDU[dataOff+4], &CDU[dataOff+5], &CDU[dataOff+6], &CDU[dataOff+7]);
}
// DCT columns
for(dataOff=0; dataOff<8; ++dataOff) {
stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+8], &CDU[dataOff+16], &CDU[dataOff+24], &CDU[dataOff+32], &CDU[dataOff+40], &CDU[dataOff+48], &CDU[dataOff+56]);
}
// Quantize/descale/zigzag the coefficients
for(i=0; i<64; ++i) {
float v = CDU[i]*fdtbl[i];
// DU[stbiw__jpg_ZigZag[i]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f));
// ceilf() and floorf() are C99, not C89, but I /think/ they're not needed here anyway?
DU[stbiw__jpg_ZigZag[i]] = (int)(v < 0 ? v - 0.5f : v + 0.5f);
}
// Encode DC
diff = DU[0] - DC;
if (diff == 0) {
stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[0]);
} else {
unsigned short bits[2];
stbiw__jpg_calcBits(diff, bits);
stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[bits[1]]);
stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits);
}
// Encode ACs
end0pos = 63;
for(; (end0pos>0)&&(DU[end0pos]==0); --end0pos) {
}
// end0pos = first element in reverse order !=0
if(end0pos == 0) {
stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB);
return DU[0];
}
for(i = 1; i <= end0pos; ++i) {
int startpos = i;
int nrzeroes;
unsigned short bits[2];
for (; DU[i]==0 && i<=end0pos; ++i) {
}
nrzeroes = i-startpos;
if ( nrzeroes >= 16 ) {
int lng = nrzeroes>>4;
int nrmarker;
for (nrmarker=1; nrmarker <= lng; ++nrmarker)
stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes);
nrzeroes &= 15;
}
stbiw__jpg_calcBits(DU[i], bits);
stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]);
stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits);
}
if(end0pos != 63) {
stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB);
}
return DU[0];
}
static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) {
// Constants that don't pollute global namespace
static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0};
static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11};
static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d};
static const unsigned char std_ac_luminance_values[] = {
0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,
0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,
0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,
0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,
0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,
0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa
};
static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0};
static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11};
static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77};
static const unsigned char std_ac_chrominance_values[] = {
0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,
0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,
0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,
0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,
0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,
0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,
0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa
};
// Huffman tables
static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}};
static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}};
static const unsigned short YAC_HT[256][2] = {
{10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0},
{2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0}
};
static const unsigned short UVAC_HT[256][2] = {
{0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0},
{1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0}
};
static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22,
37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99};
static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99,
99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99};
static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f,
1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f };
int row, col, i, k;
float fdtbl_Y[64], fdtbl_UV[64];
unsigned char YTable[64], UVTable[64];
if(!data || !width || !height || comp > 4 || comp < 1) {
return 0;
}
quality = quality ? quality : 90;
quality = quality < 1 ? 1 : quality > 100 ? 100 : quality;
quality = quality < 50 ? 5000 / quality : 200 - quality * 2;
for(i = 0; i < 64; ++i) {
int uvti, yti = (YQT[i]*quality+50)/100;
YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti);
uvti = (UVQT[i]*quality+50)/100;
UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti);
}
for(row = 0, k = 0; row < 8; ++row) {
for(col = 0; col < 8; ++col, ++k) {
fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]);
fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]);
}
}
// Write Headers
{
static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 };
static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 };
const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width),
3,1,0x11,0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 };
s->func(s->context, (void*)head0, sizeof(head0));
s->func(s->context, (void*)YTable, sizeof(YTable));
stbiw__putc(s, 1);
s->func(s->context, UVTable, sizeof(UVTable));
s->func(s->context, (void*)head1, sizeof(head1));
s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1);
s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values));
stbiw__putc(s, 0x10); // HTYACinfo
s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1);
s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values));
stbiw__putc(s, 1); // HTUDCinfo
s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1);
s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values));
stbiw__putc(s, 0x11); // HTUACinfo
s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1);
s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values));
s->func(s->context, (void*)head2, sizeof(head2));
}
// Encode 8x8 macroblocks
{
static const unsigned short fillBits[] = {0x7F, 7};
const unsigned char *imageData = (const unsigned char *)data;
int DCY=0, DCU=0, DCV=0;
int bitBuf=0, bitCnt=0;
// comp == 2 is grey+alpha (alpha is ignored)
int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0;
int x, y, pos;
for(y = 0; y < height; y += 8) {
for(x = 0; x < width; x += 8) {
float YDU[64], UDU[64], VDU[64];
for(row = y, pos = 0; row < y+8; ++row) {
// row >= height => use last input row
int clamped_row = (row < height) ? row : height - 1;
int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp;
for(col = x; col < x+8; ++col, ++pos) {
float r, g, b;
// if col >= width => use pixel from last input column
int p = base_p + ((col < width) ? col : (width-1))*comp;
r = imageData[p+0];
g = imageData[p+ofsG];
b = imageData[p+ofsB];
YDU[pos]=+0.29900f*r+0.58700f*g+0.11400f*b-128;
UDU[pos]=-0.16874f*r-0.33126f*g+0.50000f*b;
VDU[pos]=+0.50000f*r-0.41869f*g-0.08131f*b;
}
}
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT);
DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
}
}
// Do the bit alignment of the EOI marker
stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits);
}
// EOI
stbiw__putc(s, 0xFF);
stbiw__putc(s, 0xD9);
return 1;
}
STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality)
{
stbi__write_context s;
stbi__start_write_callbacks(&s, func, context);
return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality);
}
#ifndef STBI_WRITE_NO_STDIO
STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality)
{
stbi__write_context s;
if (stbi__start_write_file(&s,filename)) {
int r = stbi_write_jpg_core(&s, x, y, comp, data, quality);
stbi__end_write_file(&s);
return r;
} else
return 0;
}
#endif
#endif // STB_IMAGE_WRITE_IMPLEMENTATION #endif // STB_IMAGE_WRITE_IMPLEMENTATION
/* Revision history /* Revision history
1.10 (2019-02-07)
support utf8 filenames in Windows; fix warnings and platform ifdefs
1.09 (2018-02-11)
fix typo in zlib quality API, improve STB_I_W_STATIC in C++
1.08 (2018-01-29)
add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter
1.07 (2017-07-24)
doc fix
1.06 (2017-07-23)
writing JPEG (using Jon Olick's code)
1.05 ???
1.04 (2017-03-03) 1.04 (2017-03-03)
monochrome BMP expansion monochrome BMP expansion
1.03 ??? 1.03 ???

View file

@ -1,8 +1,7 @@
// stretchy_buffer.h - v1.03 - public domain - nothings.org/stb // stretchy_buffer.h - v1.02 - public domain - nothings.org/stb
// a vector<>-like dynamic array for C // a vector<>-like dynamic array for C
// //
// version history: // version history:
// 1.03 - compile as C++ maybe
// 1.02 - tweaks to syntax for no good reason // 1.02 - tweaks to syntax for no good reason
// 1.01 - added a "common uses" documentation section // 1.01 - added a "common uses" documentation section
// 1.0 - fixed bug in the version I posted prematurely // 1.0 - fixed bug in the version I posted prematurely
@ -163,10 +162,6 @@
// the main trick is in realizing in the first place that it's // the main trick is in realizing in the first place that it's
// possible to do this in a generic, type-safe way in C. // possible to do this in a generic, type-safe way in C.
// //
// Contributors:
//
// Timothy Wright (github:ZenToad)
//
// LICENSE // LICENSE
// //
// See end of file for license information. // See end of file for license information.
@ -194,7 +189,7 @@
#define stb__sbneedgrow(a,n) ((a)==0 || stb__sbn(a)+(n) >= stb__sbm(a)) #define stb__sbneedgrow(a,n) ((a)==0 || stb__sbn(a)+(n) >= stb__sbm(a))
#define stb__sbmaybegrow(a,n) (stb__sbneedgrow(a,(n)) ? stb__sbgrow(a,n) : 0) #define stb__sbmaybegrow(a,n) (stb__sbneedgrow(a,(n)) ? stb__sbgrow(a,n) : 0)
#define stb__sbgrow(a,n) (*((void **)&(a)) = stb__sbgrowf((a), (n), sizeof(*(a)))) #define stb__sbgrow(a,n) ((a) = stb__sbgrowf((a), (n), sizeof(*(a))))
#include <stdlib.h> #include <stdlib.h>