|
@@ -0,0 +1,1596 @@
|
|
|
+/* stbi-1.35 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c
|
|
|
+ when you control the images you're loading
|
|
|
+ no warranty implied; use at your own risk
|
|
|
+
|
|
|
+ QUICK NOTES:
|
|
|
+ Primarily of interest to game developers and other people who can
|
|
|
+ avoid problematic images and only need the trivial interface
|
|
|
+
|
|
|
+ JPEG baseline (no JPEG progressive)
|
|
|
+ PNG 8-bit-per-channel only
|
|
|
+
|
|
|
+ TGA (not sure what subset, if a subset)
|
|
|
+ BMP non-1bpp, non-RLE
|
|
|
+ PSD (composited view only, no extra channels)
|
|
|
+
|
|
|
+ GIF (*comp always reports as 4-channel)
|
|
|
+ HDR (radiance rgbE format)
|
|
|
+ PIC (Softimage PIC)
|
|
|
+
|
|
|
+ - decode from memory or through FILE (define STBI_NO_STDIO to remove code)
|
|
|
+ - decode from arbitrary I/O callbacks
|
|
|
+ - overridable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD)
|
|
|
+
|
|
|
+ Latest revisions:
|
|
|
+ 1.35 (2014-05-27) warnings, bugfixes, TGA optimization, etc
|
|
|
+ 1.34 (unknown ) warning fix
|
|
|
+ 1.33 (2011-07-14) minor fixes suggested by Dave Moore
|
|
|
+ 1.32 (2011-07-13) info support for all filetypes (SpartanJ)
|
|
|
+ 1.31 (2011-06-19) a few more leak fixes, bug in PNG handling (SpartanJ)
|
|
|
+ 1.30 (2011-06-11) added ability to load files via io callbacks (Ben Wenger)
|
|
|
+ 1.29 (2010-08-16) various warning fixes from Aurelien Pocheville
|
|
|
+ 1.28 (2010-08-01) fix bug in GIF palette transparency (SpartanJ)
|
|
|
+
|
|
|
+ See end of file for full revision history.
|
|
|
+
|
|
|
+ TODO:
|
|
|
+ stbi_info support for BMP,PSD,HDR,PIC
|
|
|
+
|
|
|
+
|
|
|
+ ============================ Contributors =========================
|
|
|
+
|
|
|
+ Image formats Bug fixes & warning fixes
|
|
|
+ Sean Barrett (jpeg, png, bmp) Marc LeBlanc
|
|
|
+ Nicolas Schulz (hdr, psd) Christpher Lloyd
|
|
|
+ Jonathan Dummer (tga) Dave Moore
|
|
|
+ Jean-Marc Lienher (gif) Won Chun
|
|
|
+ Tom Seddon (pic) the Horde3D community
|
|
|
+ Thatcher Ulrich (psd) Janez Zemva
|
|
|
+ Jonathan Blow
|
|
|
+ Laurent Gomila
|
|
|
+ Extensions, features Aruelien Pocheville
|
|
|
+ Jetro Lauha (stbi_info) Ryamond Barbiero
|
|
|
+ James "moose2000" Brown (iPhone PNG) David Woo
|
|
|
+ Ben "Disch" Wenger (io callbacks) Roy Eltham
|
|
|
+ Martin "SpartanJ" Golini Luke Graham
|
|
|
+ Thomas Ruf
|
|
|
+ John Bartholomew
|
|
|
+ Optimizations & bugfixes Ken Hamada
|
|
|
+ Fabian "ryg" Giesen Cort Stratton
|
|
|
+ Arseny Kapoulkine Blazej Dariusz Roszkowski
|
|
|
+ Thibault Reuille
|
|
|
+ If your name should be here but Paul Du Bois
|
|
|
+ isn't let Sean know. Guillaume George
|
|
|
+
|
|
|
+*/
|
|
|
+
|
|
|
+/* Modified by Alex Szpakowski to remove all decoding functionality except for
|
|
|
+ TGA and BMP formats.
|
|
|
+*/
|
|
|
+
|
|
|
+#include "stb_image.h"
|
|
|
+
|
|
|
+#ifndef STBI_NO_HDR
|
|
|
+#include <math.h> // ldexp
|
|
|
+#include <string.h> // strcmp, strtok
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifndef STBI_NO_STDIO
|
|
|
+#include <stdio.h>
|
|
|
+#endif
|
|
|
+#include <stdlib.h>
|
|
|
+#include <memory.h>
|
|
|
+#include <assert.h>
|
|
|
+#include <stdarg.h>
|
|
|
+#include <stddef.h> // ptrdiff_t on osx
|
|
|
+
|
|
|
+#ifndef _MSC_VER
|
|
|
+ #ifdef __cplusplus
|
|
|
+ #define stbi_inline inline
|
|
|
+ #else
|
|
|
+ #define stbi_inline
|
|
|
+ #endif
|
|
|
+#else
|
|
|
+ #define stbi_inline __forceinline
|
|
|
+#endif
|
|
|
+
|
|
|
+
|
|
|
+#ifdef _MSC_VER
|
|
|
+typedef unsigned char stbi__uint8;
|
|
|
+typedef unsigned short stbi__uint16;
|
|
|
+typedef signed short stbi__int16;
|
|
|
+typedef unsigned int stbi__uint32;
|
|
|
+typedef signed int stbi__int32;
|
|
|
+#else
|
|
|
+#include <stdint.h>
|
|
|
+typedef uint8_t stbi__uint8;
|
|
|
+typedef uint16_t stbi__uint16;
|
|
|
+typedef int16_t stbi__int16;
|
|
|
+typedef uint32_t stbi__uint32;
|
|
|
+typedef int32_t stbi__int32;
|
|
|
+#endif
|
|
|
+
|
|
|
+// should produce compiler error if size is wrong
|
|
|
+typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1];
|
|
|
+
|
|
|
+#ifdef _MSC_VER
|
|
|
+#define STBI_NOTUSED(v) (void)(v)
|
|
|
+#else
|
|
|
+#define STBI_NOTUSED(v) (void)sizeof(v)
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef _MSC_VER
|
|
|
+#define STBI_HAS_LROTL
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef STBI_HAS_LROTL
|
|
|
+ #define stbi_lrot(x,y) _lrotl(x,y)
|
|
|
+#else
|
|
|
+ #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y))))
|
|
|
+#endif
|
|
|
+
|
|
|
+///////////////////////////////////////////////
|
|
|
+//
|
|
|
+// stbi struct and start_xxx functions
|
|
|
+
|
|
|
+// stbi structure is our basic context used by all images, so it
|
|
|
+// contains all the IO context, plus some basic image information
|
|
|
+typedef struct
|
|
|
+{
|
|
|
+ stbi__uint32 img_x, img_y;
|
|
|
+ int img_n, img_out_n;
|
|
|
+
|
|
|
+ stbi_io_callbacks io;
|
|
|
+ void *io_user_data;
|
|
|
+
|
|
|
+ int read_from_callbacks;
|
|
|
+ int buflen;
|
|
|
+ stbi__uint8 buffer_start[128];
|
|
|
+
|
|
|
+ stbi__uint8 *img_buffer, *img_buffer_end;
|
|
|
+ stbi__uint8 *img_buffer_original;
|
|
|
+} stbi;
|
|
|
+
|
|
|
+
|
|
|
+static void refill_buffer(stbi *s);
|
|
|
+
|
|
|
+// initialize a memory-decode context
|
|
|
+static void start_mem(stbi *s, stbi__uint8 const *buffer, int len)
|
|
|
+{
|
|
|
+ s->io.read = NULL;
|
|
|
+ s->read_from_callbacks = 0;
|
|
|
+ s->img_buffer = s->img_buffer_original = (stbi__uint8 *) buffer;
|
|
|
+ s->img_buffer_end = (stbi__uint8 *) buffer+len;
|
|
|
+}
|
|
|
+
|
|
|
+// initialize a callback-based context
|
|
|
+static void start_callbacks(stbi *s, stbi_io_callbacks *c, void *user)
|
|
|
+{
|
|
|
+ s->io = *c;
|
|
|
+ s->io_user_data = user;
|
|
|
+ s->buflen = sizeof(s->buffer_start);
|
|
|
+ s->read_from_callbacks = 1;
|
|
|
+ s->img_buffer_original = s->buffer_start;
|
|
|
+ refill_buffer(s);
|
|
|
+}
|
|
|
+
|
|
|
+#ifndef STBI_NO_STDIO
|
|
|
+
|
|
|
+static int stdio_read(void *user, char *data, int size)
|
|
|
+{
|
|
|
+ return (int) fread(data,1,size,(FILE*) user);
|
|
|
+}
|
|
|
+
|
|
|
+static void stdio_skip(void *user, int n)
|
|
|
+{
|
|
|
+ fseek((FILE*) user, n, SEEK_CUR);
|
|
|
+}
|
|
|
+
|
|
|
+static int stdio_eof(void *user)
|
|
|
+{
|
|
|
+ return feof((FILE*) user);
|
|
|
+}
|
|
|
+
|
|
|
+static stbi_io_callbacks stbi_stdio_callbacks =
|
|
|
+{
|
|
|
+ stdio_read,
|
|
|
+ stdio_skip,
|
|
|
+ stdio_eof,
|
|
|
+};
|
|
|
+
|
|
|
+static void start_file(stbi *s, FILE *f)
|
|
|
+{
|
|
|
+ start_callbacks(s, &stbi_stdio_callbacks, (void *) f);
|
|
|
+}
|
|
|
+
|
|
|
+//static void stop_file(stbi *s) { }
|
|
|
+
|
|
|
+#endif // !STBI_NO_STDIO
|
|
|
+
|
|
|
+static void stbi_rewind(stbi *s)
|
|
|
+{
|
|
|
+ // conceptually rewind SHOULD rewind to the beginning of the stream,
|
|
|
+ // but we just rewind to the beginning of the initial buffer, because
|
|
|
+ // we only use it after doing 'test', which only ever looks at at most 92 bytes
|
|
|
+ s->img_buffer = s->img_buffer_original;
|
|
|
+}
|
|
|
+
|
|
|
+static int stbi_bmp_test(stbi *s);
|
|
|
+static stbi_uc *stbi_bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp);
|
|
|
+static int stbi_tga_test(stbi *s);
|
|
|
+static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp);
|
|
|
+static int stbi_tga_info(stbi *s, int *x, int *y, int *comp);
|
|
|
+#ifndef STBI_NO_HDR
|
|
|
+static int stbi_hdr_test(stbi *s);
|
|
|
+static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp);
|
|
|
+#endif
|
|
|
+
|
|
|
+
|
|
|
+// this is not threadsafe
|
|
|
+static const char *failure_reason;
|
|
|
+
|
|
|
+const char *stbi_failure_reason(void)
|
|
|
+{
|
|
|
+ return failure_reason;
|
|
|
+}
|
|
|
+
|
|
|
+static int e(const char *str)
|
|
|
+{
|
|
|
+ failure_reason = str;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+// e - error
|
|
|
+// epf - error returning pointer to float
|
|
|
+// epuc - error returning pointer to unsigned char
|
|
|
+
|
|
|
+#ifdef STBI_NO_FAILURE_STRINGS
|
|
|
+ #define e(x,y) 0
|
|
|
+#elif defined(STBI_FAILURE_USERMSG)
|
|
|
+ #define e(x,y) e(y)
|
|
|
+#else
|
|
|
+ #define e(x,y) e(x)
|
|
|
+#endif
|
|
|
+
|
|
|
+#define epf(x,y) ((float *) (e(x,y)?NULL:NULL))
|
|
|
+#define epuc(x,y) ((unsigned char *) (e(x,y)?NULL:NULL))
|
|
|
+
|
|
|
+void stbi_image_free(void *retval_from_stbi_load)
|
|
|
+{
|
|
|
+ free(retval_from_stbi_load);
|
|
|
+}
|
|
|
+
|
|
|
+#ifndef STBI_NO_HDR
|
|
|
+static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp);
|
|
|
+static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp);
|
|
|
+#endif
|
|
|
+
|
|
|
+static unsigned char *stbi_load_main(stbi *s, int *x, int *y, int *comp, int req_comp)
|
|
|
+{
|
|
|
+ if (stbi_bmp_test(s)) return stbi_bmp_load(s,x,y,comp,req_comp);
|
|
|
+
|
|
|
+ #ifndef STBI_NO_HDR
|
|
|
+ if (stbi_hdr_test(s)) {
|
|
|
+ float *hdr = stbi_hdr_load(s, x,y,comp,req_comp);
|
|
|
+ return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp);
|
|
|
+ }
|
|
|
+ #endif
|
|
|
+
|
|
|
+ // test tga last because it's a crappy test!
|
|
|
+ if (stbi_tga_test(s))
|
|
|
+ return stbi_tga_load(s,x,y,comp,req_comp);
|
|
|
+ return epuc("unknown image type", "Image not of any known type, or corrupt");
|
|
|
+}
|
|
|
+
|
|
|
+#ifndef STBI_NO_STDIO
|
|
|
+unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp)
|
|
|
+{
|
|
|
+ FILE *f = fopen(filename, "rb");
|
|
|
+ unsigned char *result;
|
|
|
+ if (!f) return epuc("can't fopen", "Unable to open file");
|
|
|
+ result = stbi_load_from_file(f,x,y,comp,req_comp);
|
|
|
+ fclose(f);
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
|
|
|
+{
|
|
|
+ unsigned char *result;
|
|
|
+ stbi s;
|
|
|
+ start_file(&s,f);
|
|
|
+ result = stbi_load_main(&s,x,y,comp,req_comp);
|
|
|
+ if (result) {
|
|
|
+ // need to 'unget' all the characters in the IO buffer
|
|
|
+ fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+}
|
|
|
+#endif //!STBI_NO_STDIO
|
|
|
+
|
|
|
+unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
|
|
|
+{
|
|
|
+ stbi s;
|
|
|
+ start_mem(&s,buffer,len);
|
|
|
+ return stbi_load_main(&s,x,y,comp,req_comp);
|
|
|
+}
|
|
|
+
|
|
|
+unsigned char *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
|
|
|
+{
|
|
|
+ stbi s;
|
|
|
+ start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
|
|
|
+ return stbi_load_main(&s,x,y,comp,req_comp);
|
|
|
+}
|
|
|
+
|
|
|
+#ifndef STBI_NO_HDR
|
|
|
+
|
|
|
+float *stbi_loadf_main(stbi *s, int *x, int *y, int *comp, int req_comp)
|
|
|
+{
|
|
|
+ unsigned char *data;
|
|
|
+ #ifndef STBI_NO_HDR
|
|
|
+ if (stbi_hdr_test(s))
|
|
|
+ return stbi_hdr_load(s,x,y,comp,req_comp);
|
|
|
+ #endif
|
|
|
+ data = stbi_load_main(s, x, y, comp, req_comp);
|
|
|
+ if (data)
|
|
|
+ return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp);
|
|
|
+ return epf("unknown image type", "Image not of any known type, or corrupt");
|
|
|
+}
|
|
|
+
|
|
|
+float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
|
|
|
+{
|
|
|
+ stbi s;
|
|
|
+ start_mem(&s,buffer,len);
|
|
|
+ return stbi_loadf_main(&s,x,y,comp,req_comp);
|
|
|
+}
|
|
|
+
|
|
|
+float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
|
|
|
+{
|
|
|
+ stbi s;
|
|
|
+ start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
|
|
|
+ return stbi_loadf_main(&s,x,y,comp,req_comp);
|
|
|
+}
|
|
|
+
|
|
|
+#ifndef STBI_NO_STDIO
|
|
|
+float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp)
|
|
|
+{
|
|
|
+ FILE *f = fopen(filename, "rb");
|
|
|
+ float *result;
|
|
|
+ if (!f) return epf("can't fopen", "Unable to open file");
|
|
|
+ result = stbi_loadf_from_file(f,x,y,comp,req_comp);
|
|
|
+ fclose(f);
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
|
|
|
+{
|
|
|
+ stbi s;
|
|
|
+ start_file(&s,f);
|
|
|
+ return stbi_loadf_main(&s,x,y,comp,req_comp);
|
|
|
+}
|
|
|
+#endif // !STBI_NO_STDIO
|
|
|
+
|
|
|
+#endif // !STBI_NO_HDR
|
|
|
+
|
|
|
+// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is
|
|
|
+// defined, for API simplicity; if STBI_NO_HDR is defined, it always
|
|
|
+// reports false!
|
|
|
+
|
|
|
+int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len)
|
|
|
+{
|
|
|
+ #ifndef STBI_NO_HDR
|
|
|
+ stbi s;
|
|
|
+ start_mem(&s,buffer,len);
|
|
|
+ return stbi_hdr_test(&s);
|
|
|
+ #else
|
|
|
+ STBI_NOTUSED(buffer);
|
|
|
+ STBI_NOTUSED(len);
|
|
|
+ return 0;
|
|
|
+ #endif
|
|
|
+}
|
|
|
+
|
|
|
+#ifndef STBI_NO_STDIO
|
|
|
+extern int stbi_is_hdr (char const *filename)
|
|
|
+{
|
|
|
+ FILE *f = fopen(filename, "rb");
|
|
|
+ int result=0;
|
|
|
+ if (f) {
|
|
|
+ result = stbi_is_hdr_from_file(f);
|
|
|
+ fclose(f);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+extern int stbi_is_hdr_from_file(FILE *f)
|
|
|
+{
|
|
|
+ #ifndef STBI_NO_HDR
|
|
|
+ stbi s;
|
|
|
+ start_file(&s,f);
|
|
|
+ return stbi_hdr_test(&s);
|
|
|
+ #else
|
|
|
+ return 0;
|
|
|
+ #endif
|
|
|
+}
|
|
|
+#endif // !STBI_NO_STDIO
|
|
|
+
|
|
|
+extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user)
|
|
|
+{
|
|
|
+ #ifndef STBI_NO_HDR
|
|
|
+ stbi s;
|
|
|
+ start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
|
|
|
+ return stbi_hdr_test(&s);
|
|
|
+ #else
|
|
|
+ (void) clbk;
|
|
|
+ (void) user;
|
|
|
+ return 0;
|
|
|
+ #endif
|
|
|
+}
|
|
|
+
|
|
|
+#ifndef STBI_NO_HDR
|
|
|
+static float h2l_gamma_i=1.0f/2.2f, h2l_scale_i=1.0f;
|
|
|
+static float l2h_gamma=2.2f, l2h_scale=1.0f;
|
|
|
+
|
|
|
+void stbi_hdr_to_ldr_gamma(float gamma) { h2l_gamma_i = 1/gamma; }
|
|
|
+void stbi_hdr_to_ldr_scale(float scale) { h2l_scale_i = 1/scale; }
|
|
|
+
|
|
|
+void stbi_ldr_to_hdr_gamma(float gamma) { l2h_gamma = gamma; }
|
|
|
+void stbi_ldr_to_hdr_scale(float scale) { l2h_scale = scale; }
|
|
|
+#endif
|
|
|
+
|
|
|
+
|
|
|
+//////////////////////////////////////////////////////////////////////////////
|
|
|
+//
|
|
|
+// Common code used by all image loaders
|
|
|
+//
|
|
|
+
|
|
|
+enum
|
|
|
+{
|
|
|
+ SCAN_load=0,
|
|
|
+ SCAN_type,
|
|
|
+ SCAN_header
|
|
|
+};
|
|
|
+
|
|
|
+static void refill_buffer(stbi *s)
|
|
|
+{
|
|
|
+ int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen);
|
|
|
+ if (n == 0) {
|
|
|
+ // at end of file, treat same as if from memory, but need to handle case
|
|
|
+ // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file
|
|
|
+ s->read_from_callbacks = 0;
|
|
|
+ s->img_buffer = s->buffer_start;
|
|
|
+ s->img_buffer_end = s->buffer_start+1;
|
|
|
+ *s->img_buffer = 0;
|
|
|
+ } else {
|
|
|
+ s->img_buffer = s->buffer_start;
|
|
|
+ s->img_buffer_end = s->buffer_start + n;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+stbi_inline static int get8(stbi *s)
|
|
|
+{
|
|
|
+ if (s->img_buffer < s->img_buffer_end)
|
|
|
+ return *s->img_buffer++;
|
|
|
+ if (s->read_from_callbacks) {
|
|
|
+ refill_buffer(s);
|
|
|
+ return *s->img_buffer++;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+#ifndef STBI_NO_HDR
|
|
|
+stbi_inline static int at_eof(stbi *s)
|
|
|
+{
|
|
|
+ if (s->io.read) {
|
|
|
+ if (!(s->io.eof)(s->io_user_data)) return 0;
|
|
|
+ // if feof() is true, check if buffer = end
|
|
|
+ // special case: we've only got the special 0 character at the end
|
|
|
+ if (s->read_from_callbacks == 0) return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return s->img_buffer >= s->img_buffer_end;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+stbi_inline static stbi__uint8 get8u(stbi *s)
|
|
|
+{
|
|
|
+ return (stbi__uint8) get8(s);
|
|
|
+}
|
|
|
+
|
|
|
+static void skip(stbi *s, int n)
|
|
|
+{
|
|
|
+ if (s->io.read) {
|
|
|
+ int blen = (int) (s->img_buffer_end - s->img_buffer);
|
|
|
+ if (blen < n) {
|
|
|
+ s->img_buffer = s->img_buffer_end;
|
|
|
+ (s->io.skip)(s->io_user_data, n - blen);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ s->img_buffer += n;
|
|
|
+}
|
|
|
+
|
|
|
+static int getn(stbi *s, stbi_uc *buffer, int n)
|
|
|
+{
|
|
|
+ if (s->io.read) {
|
|
|
+ int blen = (int) (s->img_buffer_end - s->img_buffer);
|
|
|
+ if (blen < n) {
|
|
|
+ int res, count;
|
|
|
+
|
|
|
+ memcpy(buffer, s->img_buffer, blen);
|
|
|
+
|
|
|
+ count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen);
|
|
|
+ res = (count == (n-blen));
|
|
|
+ s->img_buffer = s->img_buffer_end;
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (s->img_buffer+n <= s->img_buffer_end) {
|
|
|
+ memcpy(buffer, s->img_buffer, n);
|
|
|
+ s->img_buffer += n;
|
|
|
+ return 1;
|
|
|
+ } else
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int get16(stbi *s)
|
|
|
+{
|
|
|
+ int z = get8(s);
|
|
|
+ return (z << 8) + get8(s);
|
|
|
+}
|
|
|
+
|
|
|
+static int get16le(stbi *s)
|
|
|
+{
|
|
|
+ int z = get8(s);
|
|
|
+ return z + (get8(s) << 8);
|
|
|
+}
|
|
|
+
|
|
|
+static stbi__uint32 get32le(stbi *s)
|
|
|
+{
|
|
|
+ stbi__uint32 z = get16le(s);
|
|
|
+ return z + (get16le(s) << 16);
|
|
|
+}
|
|
|
+
|
|
|
+//////////////////////////////////////////////////////////////////////////////
|
|
|
+//
|
|
|
+// generic converter from built-in img_n to req_comp
|
|
|
+// individual types do this automatically as much as possible (e.g. jpeg
|
|
|
+// does all cases internally since it needs to colorspace convert anyway,
|
|
|
+// and it never has alpha, so very few cases ). png can automatically
|
|
|
+// interleave an alpha=255 channel, but falls back to this for other cases
|
|
|
+//
|
|
|
+// assume data buffer is malloced, so malloc a new one and free that one
|
|
|
+// only failure mode is malloc failing
|
|
|
+
|
|
|
+static stbi__uint8 compute_y(int r, int g, int b)
|
|
|
+{
|
|
|
+ return (stbi__uint8) (((r*77) + (g*150) + (29*b)) >> 8);
|
|
|
+}
|
|
|
+
|
|
|
+static unsigned char *convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y)
|
|
|
+{
|
|
|
+ int i,j;
|
|
|
+ unsigned char *good;
|
|
|
+
|
|
|
+ if (req_comp == img_n) return data;
|
|
|
+ assert(req_comp >= 1 && req_comp <= 4);
|
|
|
+
|
|
|
+ good = (unsigned char *) malloc(req_comp * x * y);
|
|
|
+ if (good == NULL) {
|
|
|
+ free(data);
|
|
|
+ return epuc("outofmem", "Out of memory");
|
|
|
+ }
|
|
|
+
|
|
|
+ for (j=0; j < (int) y; ++j) {
|
|
|
+ unsigned char *src = data + j * x * img_n ;
|
|
|
+ unsigned char *dest = good + j * x * req_comp;
|
|
|
+
|
|
|
+ #define COMBO(a,b) ((a)*8+(b))
|
|
|
+ #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)
|
|
|
+ // convert source image with img_n components to one with req_comp components;
|
|
|
+ // avoid switch per pixel, so use switch per scanline and massive macros
|
|
|
+ switch (COMBO(img_n, req_comp)) {
|
|
|
+ CASE(1,2) dest[0]=src[0], dest[1]=255; break;
|
|
|
+ CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break;
|
|
|
+ CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break;
|
|
|
+ CASE(2,1) dest[0]=src[0]; break;
|
|
|
+ CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break;
|
|
|
+ CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break;
|
|
|
+ CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break;
|
|
|
+ CASE(3,1) dest[0]=compute_y(src[0],src[1],src[2]); break;
|
|
|
+ CASE(3,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = 255; break;
|
|
|
+ CASE(4,1) dest[0]=compute_y(src[0],src[1],src[2]); break;
|
|
|
+ CASE(4,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break;
|
|
|
+ CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break;
|
|
|
+ default: assert(0);
|
|
|
+ }
|
|
|
+ #undef CASE
|
|
|
+ }
|
|
|
+
|
|
|
+ free(data);
|
|
|
+ return good;
|
|
|
+}
|
|
|
+
|
|
|
+#ifndef STBI_NO_HDR
|
|
|
+static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp)
|
|
|
+{
|
|
|
+ int i,k,n;
|
|
|
+ float *output = (float *) malloc(x * y * comp * sizeof(float));
|
|
|
+ if (output == NULL) { free(data); return epf("outofmem", "Out of memory"); }
|
|
|
+ // compute number of non-alpha components
|
|
|
+ if (comp & 1) n = comp; else n = comp-1;
|
|
|
+ for (i=0; i < x*y; ++i) {
|
|
|
+ for (k=0; k < n; ++k) {
|
|
|
+ output[i*comp + k] = (float) pow(data[i*comp+k]/255.0f, l2h_gamma) * l2h_scale;
|
|
|
+ }
|
|
|
+ if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f;
|
|
|
+ }
|
|
|
+ free(data);
|
|
|
+ return output;
|
|
|
+}
|
|
|
+
|
|
|
+#define float2int(x) ((int) (x))
|
|
|
+static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp)
|
|
|
+{
|
|
|
+ int i,k,n;
|
|
|
+ stbi_uc *output = (stbi_uc *) malloc(x * y * comp);
|
|
|
+ if (output == NULL) { free(data); return epuc("outofmem", "Out of memory"); }
|
|
|
+ // compute number of non-alpha components
|
|
|
+ if (comp & 1) n = comp; else n = comp-1;
|
|
|
+ for (i=0; i < x*y; ++i) {
|
|
|
+ for (k=0; k < n; ++k) {
|
|
|
+ float z = (float) pow(data[i*comp+k]*h2l_scale_i, h2l_gamma_i) * 255 + 0.5f;
|
|
|
+ if (z < 0) z = 0;
|
|
|
+ if (z > 255) z = 255;
|
|
|
+ output[i*comp + k] = (stbi__uint8) float2int(z);
|
|
|
+ }
|
|
|
+ if (k < comp) {
|
|
|
+ float z = data[i*comp+k] * 255 + 0.5f;
|
|
|
+ if (z < 0) z = 0;
|
|
|
+ if (z > 255) z = 255;
|
|
|
+ output[i*comp + k] = (stbi__uint8) float2int(z);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ free(data);
|
|
|
+ return output;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+
|
|
|
+// Microsoft/Windows BMP image
|
|
|
+
|
|
|
+static int bmp_test(stbi *s)
|
|
|
+{
|
|
|
+ int sz;
|
|
|
+ if (get8(s) != 'B') return 0;
|
|
|
+ if (get8(s) != 'M') return 0;
|
|
|
+ get32le(s); // discard filesize
|
|
|
+ get16le(s); // discard reserved
|
|
|
+ get16le(s); // discard reserved
|
|
|
+ get32le(s); // discard data offset
|
|
|
+ sz = get32le(s);
|
|
|
+ if (sz == 12 || sz == 40 || sz == 56 || sz == 108) return 1;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int stbi_bmp_test(stbi *s)
|
|
|
+{
|
|
|
+ int r = bmp_test(s);
|
|
|
+ stbi_rewind(s);
|
|
|
+ return r;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// returns 0..31 for the highest set bit
|
|
|
+static int high_bit(unsigned int z)
|
|
|
+{
|
|
|
+ int n=0;
|
|
|
+ if (z == 0) return -1;
|
|
|
+ if (z >= 0x10000) n += 16, z >>= 16;
|
|
|
+ if (z >= 0x00100) n += 8, z >>= 8;
|
|
|
+ if (z >= 0x00010) n += 4, z >>= 4;
|
|
|
+ if (z >= 0x00004) n += 2, z >>= 2;
|
|
|
+ if (z >= 0x00002) n += 1, z >>= 1;
|
|
|
+ return n;
|
|
|
+}
|
|
|
+
|
|
|
+static int bitcount(unsigned int a)
|
|
|
+{
|
|
|
+ a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2
|
|
|
+ a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4
|
|
|
+ a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits
|
|
|
+ a = (a + (a >> 8)); // max 16 per 8 bits
|
|
|
+ a = (a + (a >> 16)); // max 32 per 8 bits
|
|
|
+ return a & 0xff;
|
|
|
+}
|
|
|
+
|
|
|
+static int shiftsigned(int v, int shift, int bits)
|
|
|
+{
|
|
|
+ int result;
|
|
|
+ int z=0;
|
|
|
+
|
|
|
+ if (shift < 0) v <<= -shift;
|
|
|
+ else v >>= shift;
|
|
|
+ result = v;
|
|
|
+
|
|
|
+ z = bits;
|
|
|
+ while (z < 8) {
|
|
|
+ result += v >> z;
|
|
|
+ z += bits;
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp)
|
|
|
+{
|
|
|
+ stbi__uint8 *out;
|
|
|
+ unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0;
|
|
|
+ stbi_uc pal[256][4];
|
|
|
+ int psize=0,i,j,compress=0,width;
|
|
|
+ int bpp, flip_vertically, pad, target, offset, hsz;
|
|
|
+ if (get8(s) != 'B' || get8(s) != 'M') return epuc("not BMP", "Corrupt BMP");
|
|
|
+ get32le(s); // discard filesize
|
|
|
+ get16le(s); // discard reserved
|
|
|
+ get16le(s); // discard reserved
|
|
|
+ offset = get32le(s);
|
|
|
+ hsz = get32le(s);
|
|
|
+ if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return epuc("unknown BMP", "BMP type not supported: unknown");
|
|
|
+ if (hsz == 12) {
|
|
|
+ s->img_x = get16le(s);
|
|
|
+ s->img_y = get16le(s);
|
|
|
+ } else {
|
|
|
+ s->img_x = get32le(s);
|
|
|
+ s->img_y = get32le(s);
|
|
|
+ }
|
|
|
+ if (get16le(s) != 1) return epuc("bad BMP", "bad BMP");
|
|
|
+ bpp = get16le(s);
|
|
|
+ if (bpp == 1) return epuc("monochrome", "BMP type not supported: 1-bit");
|
|
|
+ flip_vertically = ((int) s->img_y) > 0;
|
|
|
+ s->img_y = abs((int) s->img_y);
|
|
|
+ if (hsz == 12) {
|
|
|
+ if (bpp < 24)
|
|
|
+ psize = (offset - 14 - 24) / 3;
|
|
|
+ } else {
|
|
|
+ compress = get32le(s);
|
|
|
+ if (compress == 1 || compress == 2) return epuc("BMP RLE", "BMP type not supported: RLE");
|
|
|
+ get32le(s); // discard sizeof
|
|
|
+ get32le(s); // discard hres
|
|
|
+ get32le(s); // discard vres
|
|
|
+ get32le(s); // discard colorsused
|
|
|
+ get32le(s); // discard max important
|
|
|
+ if (hsz == 40 || hsz == 56) {
|
|
|
+ if (hsz == 56) {
|
|
|
+ get32le(s);
|
|
|
+ get32le(s);
|
|
|
+ get32le(s);
|
|
|
+ get32le(s);
|
|
|
+ }
|
|
|
+ if (bpp == 16 || bpp == 32) {
|
|
|
+ mr = mg = mb = 0;
|
|
|
+ if (compress == 0) {
|
|
|
+ if (bpp == 32) {
|
|
|
+ mr = 0xffu << 16;
|
|
|
+ mg = 0xffu << 8;
|
|
|
+ mb = 0xffu << 0;
|
|
|
+ ma = 0xffu << 24;
|
|
|
+ fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255
|
|
|
+ STBI_NOTUSED(fake_a);
|
|
|
+ } else {
|
|
|
+ mr = 31u << 10;
|
|
|
+ mg = 31u << 5;
|
|
|
+ mb = 31u << 0;
|
|
|
+ }
|
|
|
+ } else if (compress == 3) {
|
|
|
+ mr = get32le(s);
|
|
|
+ mg = get32le(s);
|
|
|
+ mb = get32le(s);
|
|
|
+ // not documented, but generated by photoshop and handled by mspaint
|
|
|
+ if (mr == mg && mg == mb) {
|
|
|
+ // ?!?!?
|
|
|
+ return epuc("bad BMP", "bad BMP");
|
|
|
+ }
|
|
|
+ } else
|
|
|
+ return epuc("bad BMP", "bad BMP");
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ assert(hsz == 108);
|
|
|
+ mr = get32le(s);
|
|
|
+ mg = get32le(s);
|
|
|
+ mb = get32le(s);
|
|
|
+ ma = get32le(s);
|
|
|
+ get32le(s); // discard color space
|
|
|
+ for (i=0; i < 12; ++i)
|
|
|
+ get32le(s); // discard color space parameters
|
|
|
+ }
|
|
|
+ if (bpp < 16)
|
|
|
+ psize = (offset - 14 - hsz) >> 2;
|
|
|
+ }
|
|
|
+ s->img_n = ma ? 4 : 3;
|
|
|
+ if (req_comp && req_comp >= 3) // we can directly decode 3 or 4
|
|
|
+ target = req_comp;
|
|
|
+ else
|
|
|
+ target = s->img_n; // if they want monochrome, we'll post-convert
|
|
|
+ out = (stbi_uc *) malloc(target * s->img_x * s->img_y);
|
|
|
+ if (!out) return epuc("outofmem", "Out of memory");
|
|
|
+ if (bpp < 16) {
|
|
|
+ int z=0;
|
|
|
+ if (psize == 0 || psize > 256) { free(out); return epuc("invalid", "Corrupt BMP"); }
|
|
|
+ for (i=0; i < psize; ++i) {
|
|
|
+ pal[i][2] = get8u(s);
|
|
|
+ pal[i][1] = get8u(s);
|
|
|
+ pal[i][0] = get8u(s);
|
|
|
+ if (hsz != 12) get8(s);
|
|
|
+ pal[i][3] = 255;
|
|
|
+ }
|
|
|
+ skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4));
|
|
|
+ if (bpp == 4) width = (s->img_x + 1) >> 1;
|
|
|
+ else if (bpp == 8) width = s->img_x;
|
|
|
+ else { free(out); return epuc("bad bpp", "Corrupt BMP"); }
|
|
|
+ pad = (-width)&3;
|
|
|
+ for (j=0; j < (int) s->img_y; ++j) {
|
|
|
+ for (i=0; i < (int) s->img_x; i += 2) {
|
|
|
+ int v=get8(s),v2=0;
|
|
|
+ if (bpp == 4) {
|
|
|
+ v2 = v & 15;
|
|
|
+ v >>= 4;
|
|
|
+ }
|
|
|
+ out[z++] = pal[v][0];
|
|
|
+ out[z++] = pal[v][1];
|
|
|
+ out[z++] = pal[v][2];
|
|
|
+ if (target == 4) out[z++] = 255;
|
|
|
+ if (i+1 == (int) s->img_x) break;
|
|
|
+ v = (bpp == 8) ? get8(s) : v2;
|
|
|
+ out[z++] = pal[v][0];
|
|
|
+ out[z++] = pal[v][1];
|
|
|
+ out[z++] = pal[v][2];
|
|
|
+ if (target == 4) out[z++] = 255;
|
|
|
+ }
|
|
|
+ skip(s, pad);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0;
|
|
|
+ int z = 0;
|
|
|
+ int easy=0;
|
|
|
+ skip(s, offset - 14 - hsz);
|
|
|
+ if (bpp == 24) width = 3 * s->img_x;
|
|
|
+ else if (bpp == 16) width = 2*s->img_x;
|
|
|
+ else /* bpp = 32 and pad = 0 */ width=0;
|
|
|
+ pad = (-width) & 3;
|
|
|
+ if (bpp == 24) {
|
|
|
+ easy = 1;
|
|
|
+ } else if (bpp == 32) {
|
|
|
+ if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000)
|
|
|
+ easy = 2;
|
|
|
+ }
|
|
|
+ if (!easy) {
|
|
|
+ if (!mr || !mg || !mb) { free(out); return epuc("bad masks", "Corrupt BMP"); }
|
|
|
+ // right shift amt to put high bit in position #7
|
|
|
+ rshift = high_bit(mr)-7; rcount = bitcount(mr);
|
|
|
+ gshift = high_bit(mg)-7; gcount = bitcount(mg);
|
|
|
+ bshift = high_bit(mb)-7; bcount = bitcount(mb);
|
|
|
+ ashift = high_bit(ma)-7; acount = bitcount(ma);
|
|
|
+ }
|
|
|
+ for (j=0; j < (int) s->img_y; ++j) {
|
|
|
+ if (easy) {
|
|
|
+ for (i=0; i < (int) s->img_x; ++i) {
|
|
|
+ int a;
|
|
|
+ out[z+2] = get8u(s);
|
|
|
+ out[z+1] = get8u(s);
|
|
|
+ out[z+0] = get8u(s);
|
|
|
+ z += 3;
|
|
|
+ a = (easy == 2 ? get8(s) : 255);
|
|
|
+ if (target == 4) out[z++] = (stbi__uint8) a;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ for (i=0; i < (int) s->img_x; ++i) {
|
|
|
+ stbi__uint32 v = (stbi__uint32) (bpp == 16 ? get16le(s) : get32le(s));
|
|
|
+ int a;
|
|
|
+ out[z++] = (stbi__uint8) shiftsigned(v & mr, rshift, rcount);
|
|
|
+ out[z++] = (stbi__uint8) shiftsigned(v & mg, gshift, gcount);
|
|
|
+ out[z++] = (stbi__uint8) shiftsigned(v & mb, bshift, bcount);
|
|
|
+ a = (ma ? shiftsigned(v & ma, ashift, acount) : 255);
|
|
|
+ if (target == 4) out[z++] = (stbi__uint8) a;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ skip(s, pad);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (flip_vertically) {
|
|
|
+ stbi_uc t;
|
|
|
+ for (j=0; j < (int) s->img_y>>1; ++j) {
|
|
|
+ stbi_uc *p1 = out + j *s->img_x*target;
|
|
|
+ stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target;
|
|
|
+ for (i=0; i < (int) s->img_x*target; ++i) {
|
|
|
+ t = p1[i], p1[i] = p2[i], p2[i] = t;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (req_comp && req_comp != target) {
|
|
|
+ out = convert_format(out, target, req_comp, s->img_x, s->img_y);
|
|
|
+ if (out == NULL) return out; // convert_format frees input on failure
|
|
|
+ }
|
|
|
+
|
|
|
+ *x = s->img_x;
|
|
|
+ *y = s->img_y;
|
|
|
+ if (comp) *comp = s->img_n;
|
|
|
+ return out;
|
|
|
+}
|
|
|
+
|
|
|
+static stbi_uc *stbi_bmp_load(stbi *s,int *x, int *y, int *comp, int req_comp)
|
|
|
+{
|
|
|
+ return bmp_load(s, x,y,comp,req_comp);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// Targa Truevision - TGA
|
|
|
+// by Jonathan Dummer
|
|
|
+
|
|
|
+static int tga_info(stbi *s, int *x, int *y, int *comp)
|
|
|
+{
|
|
|
+ int tga_w, tga_h, tga_comp;
|
|
|
+ int sz;
|
|
|
+ get8u(s); // discard Offset
|
|
|
+ sz = get8u(s); // color type
|
|
|
+ if( sz > 1 ) {
|
|
|
+ stbi_rewind(s);
|
|
|
+ return 0; // only RGB or indexed allowed
|
|
|
+ }
|
|
|
+ sz = get8u(s); // image type
|
|
|
+ // only RGB or grey allowed, +/- RLE
|
|
|
+ if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0;
|
|
|
+ skip(s,9);
|
|
|
+ tga_w = get16le(s);
|
|
|
+ if( tga_w < 1 ) {
|
|
|
+ stbi_rewind(s);
|
|
|
+ return 0; // test width
|
|
|
+ }
|
|
|
+ tga_h = get16le(s);
|
|
|
+ if( tga_h < 1 ) {
|
|
|
+ stbi_rewind(s);
|
|
|
+ return 0; // test height
|
|
|
+ }
|
|
|
+ sz = get8(s); // bits per pixel
|
|
|
+ // only RGB or RGBA or grey allowed
|
|
|
+ if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) {
|
|
|
+ stbi_rewind(s);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ tga_comp = sz;
|
|
|
+ if (x) *x = tga_w;
|
|
|
+ if (y) *y = tga_h;
|
|
|
+ if (comp) *comp = tga_comp / 8;
|
|
|
+ return 1; // seems to have passed everything
|
|
|
+}
|
|
|
+
|
|
|
+int stbi_tga_info(stbi *s, int *x, int *y, int *comp)
|
|
|
+{
|
|
|
+ return tga_info(s, x, y, comp);
|
|
|
+}
|
|
|
+
|
|
|
+static int tga_test(stbi *s)
|
|
|
+{
|
|
|
+ int sz;
|
|
|
+ get8u(s); // discard Offset
|
|
|
+ sz = get8u(s); // color type
|
|
|
+ if ( sz > 1 ) return 0; // only RGB or indexed allowed
|
|
|
+ sz = get8u(s); // image type
|
|
|
+ if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE
|
|
|
+ get16(s); // discard palette start
|
|
|
+ get16(s); // discard palette length
|
|
|
+ get8(s); // discard bits per palette color entry
|
|
|
+ get16(s); // discard x origin
|
|
|
+ get16(s); // discard y origin
|
|
|
+ if ( get16(s) < 1 ) return 0; // test width
|
|
|
+ if ( get16(s) < 1 ) return 0; // test height
|
|
|
+ sz = get8(s); // bits per pixel
|
|
|
+ if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) return 0; // only RGB or RGBA or grey allowed
|
|
|
+ return 1; // seems to have passed everything
|
|
|
+}
|
|
|
+
|
|
|
+static int stbi_tga_test(stbi *s)
|
|
|
+{
|
|
|
+ int res = tga_test(s);
|
|
|
+ stbi_rewind(s);
|
|
|
+ return res;
|
|
|
+}
|
|
|
+
|
|
|
+static stbi_uc *tga_load(stbi *s, int *x, int *y, int *comp, int req_comp)
|
|
|
+{
|
|
|
+ // read in the TGA header stuff
|
|
|
+ int tga_offset = get8u(s);
|
|
|
+ int tga_indexed = get8u(s);
|
|
|
+ int tga_image_type = get8u(s);
|
|
|
+ int tga_is_RLE = 0;
|
|
|
+ int tga_palette_start = get16le(s);
|
|
|
+ int tga_palette_len = get16le(s);
|
|
|
+ int tga_palette_bits = get8u(s);
|
|
|
+ int tga_x_origin = get16le(s);
|
|
|
+ int tga_y_origin = get16le(s);
|
|
|
+ int tga_width = get16le(s);
|
|
|
+ int tga_height = get16le(s);
|
|
|
+ int tga_bits_per_pixel = get8u(s);
|
|
|
+ int tga_comp = tga_bits_per_pixel / 8;
|
|
|
+ int tga_inverted = get8u(s);
|
|
|
+ // image data
|
|
|
+ unsigned char *tga_data;
|
|
|
+ unsigned char *tga_palette = NULL;
|
|
|
+ int i, j;
|
|
|
+ unsigned char raw_data[4];
|
|
|
+ int RLE_count = 0;
|
|
|
+ int RLE_repeating = 0;
|
|
|
+ int read_next_pixel = 1;
|
|
|
+
|
|
|
+ // do a tiny bit of precessing
|
|
|
+ if ( tga_image_type >= 8 )
|
|
|
+ {
|
|
|
+ tga_image_type -= 8;
|
|
|
+ tga_is_RLE = 1;
|
|
|
+ }
|
|
|
+ /* int tga_alpha_bits = tga_inverted & 15; */
|
|
|
+ tga_inverted = 1 - ((tga_inverted >> 5) & 1);
|
|
|
+
|
|
|
+ // error check
|
|
|
+ if ( //(tga_indexed) ||
|
|
|
+ (tga_width < 1) || (tga_height < 1) ||
|
|
|
+ (tga_image_type < 1) || (tga_image_type > 3) ||
|
|
|
+ ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) &&
|
|
|
+ (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32))
|
|
|
+ )
|
|
|
+ {
|
|
|
+ return NULL; // we don't report this as a bad TGA because we don't even know if it's TGA
|
|
|
+ }
|
|
|
+
|
|
|
+ // If I'm paletted, then I'll use the number of bits from the palette
|
|
|
+ if ( tga_indexed )
|
|
|
+ {
|
|
|
+ tga_comp = tga_palette_bits / 8;
|
|
|
+ }
|
|
|
+
|
|
|
+ // tga info
|
|
|
+ *x = tga_width;
|
|
|
+ *y = tga_height;
|
|
|
+ if (comp) *comp = tga_comp;
|
|
|
+
|
|
|
+ tga_data = (unsigned char*)malloc( tga_width * tga_height * req_comp );
|
|
|
+ if (!tga_data) return epuc("outofmem", "Out of memory");
|
|
|
+
|
|
|
+ // skip to the data's starting position (offset usually = 0)
|
|
|
+ skip(s, tga_offset );
|
|
|
+
|
|
|
+ if ( !tga_indexed && !tga_is_RLE) {
|
|
|
+ for (i=0; i < tga_height; ++i) {
|
|
|
+ int y = tga_inverted ? tga_height -i - 1 : i;
|
|
|
+ stbi__uint8 *tga_row = tga_data + y*tga_width*tga_comp;
|
|
|
+ getn(s, tga_row, tga_width * tga_comp);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // do I need to load a palette?
|
|
|
+ if ( tga_indexed)
|
|
|
+ {
|
|
|
+ // any data to skip? (offset usually = 0)
|
|
|
+ skip(s, tga_palette_start );
|
|
|
+ // load the palette
|
|
|
+ tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 );
|
|
|
+ if (!tga_palette) {
|
|
|
+ free(tga_data);
|
|
|
+ return epuc("outofmem", "Out of memory");
|
|
|
+ }
|
|
|
+ if (!getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) {
|
|
|
+ free(tga_data);
|
|
|
+ free(tga_palette);
|
|
|
+ return epuc("bad palette", "Corrupt TGA");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // load the data
|
|
|
+ for (i=0; i < tga_width * tga_height; ++i)
|
|
|
+ {
|
|
|
+ // if I'm in RLE mode, do I need to get a RLE chunk?
|
|
|
+ if ( tga_is_RLE )
|
|
|
+ {
|
|
|
+ if ( RLE_count == 0 )
|
|
|
+ {
|
|
|
+ // yep, get the next byte as a RLE command
|
|
|
+ int RLE_cmd = get8u(s);
|
|
|
+ RLE_count = 1 + (RLE_cmd & 127);
|
|
|
+ RLE_repeating = RLE_cmd >> 7;
|
|
|
+ read_next_pixel = 1;
|
|
|
+ } else if ( !RLE_repeating )
|
|
|
+ {
|
|
|
+ read_next_pixel = 1;
|
|
|
+ }
|
|
|
+ } else
|
|
|
+ {
|
|
|
+ read_next_pixel = 1;
|
|
|
+ }
|
|
|
+ // OK, if I need to read a pixel, do it now
|
|
|
+ if ( read_next_pixel )
|
|
|
+ {
|
|
|
+ // load however much data we did have
|
|
|
+ if ( tga_indexed )
|
|
|
+ {
|
|
|
+ // read in 1 byte, then perform the lookup
|
|
|
+ int pal_idx = get8u(s);
|
|
|
+ if ( pal_idx >= tga_palette_len )
|
|
|
+ {
|
|
|
+ // invalid index
|
|
|
+ pal_idx = 0;
|
|
|
+ }
|
|
|
+ pal_idx *= tga_bits_per_pixel / 8;
|
|
|
+ for (j = 0; j*8 < tga_bits_per_pixel; ++j)
|
|
|
+ {
|
|
|
+ raw_data[j] = tga_palette[pal_idx+j];
|
|
|
+ }
|
|
|
+ } else
|
|
|
+ {
|
|
|
+ // read in the data raw
|
|
|
+ for (j = 0; j*8 < tga_bits_per_pixel; ++j)
|
|
|
+ {
|
|
|
+ raw_data[j] = get8u(s);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // clear the reading flag for the next pixel
|
|
|
+ read_next_pixel = 0;
|
|
|
+ } // end of reading a pixel
|
|
|
+
|
|
|
+ // copy data
|
|
|
+ for (j = 0; j < tga_comp; ++j)
|
|
|
+ tga_data[i*tga_comp+j] = raw_data[j];
|
|
|
+
|
|
|
+ // in case we're in RLE mode, keep counting down
|
|
|
+ --RLE_count;
|
|
|
+ }
|
|
|
+ // do I need to invert the image?
|
|
|
+ if ( tga_inverted )
|
|
|
+ {
|
|
|
+ for (j = 0; j*2 < tga_height; ++j)
|
|
|
+ {
|
|
|
+ int index1 = j * tga_width * req_comp;
|
|
|
+ int index2 = (tga_height - 1 - j) * tga_width * req_comp;
|
|
|
+ for (i = tga_width * req_comp; i > 0; --i)
|
|
|
+ {
|
|
|
+ unsigned char temp = tga_data[index1];
|
|
|
+ tga_data[index1] = tga_data[index2];
|
|
|
+ tga_data[index2] = temp;
|
|
|
+ ++index1;
|
|
|
+ ++index2;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // clear my palette, if I had one
|
|
|
+ if ( tga_palette != NULL )
|
|
|
+ {
|
|
|
+ free( tga_palette );
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // swap RGB
|
|
|
+ if (tga_comp >= 3)
|
|
|
+ {
|
|
|
+ unsigned char* tga_pixel = tga_data;
|
|
|
+ for (i=0; i < tga_width * tga_height; ++i)
|
|
|
+ {
|
|
|
+ unsigned char temp = tga_pixel[0];
|
|
|
+ tga_pixel[0] = tga_pixel[2];
|
|
|
+ tga_pixel[2] = temp;
|
|
|
+ tga_pixel += tga_comp;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // convert to target component count
|
|
|
+ if (req_comp && req_comp != tga_comp)
|
|
|
+ tga_data = convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height);
|
|
|
+
|
|
|
+ // the things I do to get rid of an error message, and yet keep
|
|
|
+ // Microsoft's C compilers happy... [8^(
|
|
|
+ tga_palette_start = tga_palette_len = tga_palette_bits =
|
|
|
+ tga_x_origin = tga_y_origin = 0;
|
|
|
+ // OK, done
|
|
|
+ return tga_data;
|
|
|
+}
|
|
|
+
|
|
|
+static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp)
|
|
|
+{
|
|
|
+ return tga_load(s,x,y,comp,req_comp);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// *************************************************************************************************
|
|
|
+// Radiance RGBE HDR loader
|
|
|
+// originally by Nicolas Schulz
|
|
|
+#ifndef STBI_NO_HDR
|
|
|
+static int hdr_test(stbi *s)
|
|
|
+{
|
|
|
+ const char *signature = "#?RADIANCE\n";
|
|
|
+ int i;
|
|
|
+ for (i=0; signature[i]; ++i)
|
|
|
+ if (get8(s) != signature[i])
|
|
|
+ return 0;
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+static int stbi_hdr_test(stbi* s)
|
|
|
+{
|
|
|
+ int r = hdr_test(s);
|
|
|
+ stbi_rewind(s);
|
|
|
+ return r;
|
|
|
+}
|
|
|
+
|
|
|
+#define HDR_BUFLEN 1024
|
|
|
+static char *hdr_gettoken(stbi *z, char *buffer)
|
|
|
+{
|
|
|
+ int len=0;
|
|
|
+ char c = '\0';
|
|
|
+
|
|
|
+ c = (char) get8(z);
|
|
|
+
|
|
|
+ while (!at_eof(z) && c != '\n') {
|
|
|
+ buffer[len++] = c;
|
|
|
+ if (len == HDR_BUFLEN-1) {
|
|
|
+ // flush to end of line
|
|
|
+ while (!at_eof(z) && get8(z) != '\n')
|
|
|
+ ;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ c = (char) get8(z);
|
|
|
+ }
|
|
|
+
|
|
|
+ buffer[len] = 0;
|
|
|
+ return buffer;
|
|
|
+}
|
|
|
+
|
|
|
+static void hdr_convert(float *output, stbi_uc *input, int req_comp)
|
|
|
+{
|
|
|
+ if ( input[3] != 0 ) {
|
|
|
+ float f1;
|
|
|
+ // Exponent
|
|
|
+ f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8));
|
|
|
+ if (req_comp <= 2)
|
|
|
+ output[0] = (input[0] + input[1] + input[2]) * f1 / 3;
|
|
|
+ else {
|
|
|
+ output[0] = input[0] * f1;
|
|
|
+ output[1] = input[1] * f1;
|
|
|
+ output[2] = input[2] * f1;
|
|
|
+ }
|
|
|
+ if (req_comp == 2) output[1] = 1;
|
|
|
+ if (req_comp == 4) output[3] = 1;
|
|
|
+ } else {
|
|
|
+ switch (req_comp) {
|
|
|
+ case 4: output[3] = 1; /* fallthrough */
|
|
|
+ case 3: output[0] = output[1] = output[2] = 0;
|
|
|
+ break;
|
|
|
+ case 2: output[1] = 1; /* fallthrough */
|
|
|
+ case 1: output[0] = 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static float *hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp)
|
|
|
+{
|
|
|
+ char buffer[HDR_BUFLEN];
|
|
|
+ char *token;
|
|
|
+ int valid = 0;
|
|
|
+ int width, height;
|
|
|
+ stbi_uc *scanline;
|
|
|
+ float *hdr_data;
|
|
|
+ int len;
|
|
|
+ unsigned char count, value;
|
|
|
+ int i, j, k, c1,c2, z;
|
|
|
+
|
|
|
+
|
|
|
+ // Check identifier
|
|
|
+ if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0)
|
|
|
+ return epf("not HDR", "Corrupt HDR image");
|
|
|
+
|
|
|
+ // Parse header
|
|
|
+ for(;;) {
|
|
|
+ token = hdr_gettoken(s,buffer);
|
|
|
+ if (token[0] == 0) break;
|
|
|
+ if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!valid) return epf("unsupported format", "Unsupported HDR format");
|
|
|
+
|
|
|
+ // Parse width and height
|
|
|
+ // can't use sscanf() if we're not using stdio!
|
|
|
+ token = hdr_gettoken(s,buffer);
|
|
|
+ if (strncmp(token, "-Y ", 3)) return epf("unsupported data layout", "Unsupported HDR format");
|
|
|
+ token += 3;
|
|
|
+ height = (int) strtol(token, &token, 10);
|
|
|
+ while (*token == ' ') ++token;
|
|
|
+ if (strncmp(token, "+X ", 3)) return epf("unsupported data layout", "Unsupported HDR format");
|
|
|
+ token += 3;
|
|
|
+ width = (int) strtol(token, NULL, 10);
|
|
|
+
|
|
|
+ *x = width;
|
|
|
+ *y = height;
|
|
|
+
|
|
|
+ *comp = 3;
|
|
|
+ if (req_comp == 0) req_comp = 3;
|
|
|
+
|
|
|
+ // Read data
|
|
|
+ hdr_data = (float *) malloc(height * width * req_comp * sizeof(float));
|
|
|
+
|
|
|
+ // Load image data
|
|
|
+ // image data is stored as some number of sca
|
|
|
+ if ( width < 8 || width >= 32768) {
|
|
|
+ // Read flat data
|
|
|
+ for (j=0; j < height; ++j) {
|
|
|
+ for (i=0; i < width; ++i) {
|
|
|
+ stbi_uc rgbe[4];
|
|
|
+ main_decode_loop:
|
|
|
+ getn(s, rgbe, 4);
|
|
|
+ hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // Read RLE-encoded data
|
|
|
+ scanline = NULL;
|
|
|
+
|
|
|
+ for (j = 0; j < height; ++j) {
|
|
|
+ c1 = get8(s);
|
|
|
+ c2 = get8(s);
|
|
|
+ len = get8(s);
|
|
|
+ if (c1 != 2 || c2 != 2 || (len & 0x80)) {
|
|
|
+ // not run-length encoded, so we have to actually use THIS data as a decoded
|
|
|
+ // pixel (note this can't be a valid pixel--one of RGB must be >= 128)
|
|
|
+ stbi__uint8 rgbe[4];
|
|
|
+ rgbe[0] = (stbi__uint8) c1;
|
|
|
+ rgbe[1] = (stbi__uint8) c2;
|
|
|
+ rgbe[2] = (stbi__uint8) len;
|
|
|
+ rgbe[3] = (stbi__uint8) get8u(s);
|
|
|
+ hdr_convert(hdr_data, rgbe, req_comp);
|
|
|
+ i = 1;
|
|
|
+ j = 0;
|
|
|
+ free(scanline);
|
|
|
+ goto main_decode_loop; // yes, this makes no sense
|
|
|
+ }
|
|
|
+ len <<= 8;
|
|
|
+ len |= get8(s);
|
|
|
+ if (len != width) { free(hdr_data); free(scanline); return epf("invalid decoded scanline length", "corrupt HDR"); }
|
|
|
+ if (scanline == NULL) scanline = (stbi_uc *) malloc(width * 4);
|
|
|
+
|
|
|
+ for (k = 0; k < 4; ++k) {
|
|
|
+ i = 0;
|
|
|
+ while (i < width) {
|
|
|
+ count = get8u(s);
|
|
|
+ if (count > 128) {
|
|
|
+ // Run
|
|
|
+ value = get8u(s);
|
|
|
+ count -= 128;
|
|
|
+ for (z = 0; z < count; ++z)
|
|
|
+ scanline[i++ * 4 + k] = value;
|
|
|
+ } else {
|
|
|
+ // Dump
|
|
|
+ for (z = 0; z < count; ++z)
|
|
|
+ scanline[i++ * 4 + k] = get8u(s);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (i=0; i < width; ++i)
|
|
|
+ hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp);
|
|
|
+ }
|
|
|
+ free(scanline);
|
|
|
+ }
|
|
|
+
|
|
|
+ return hdr_data;
|
|
|
+}
|
|
|
+
|
|
|
+static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp)
|
|
|
+{
|
|
|
+ return hdr_load(s,x,y,comp,req_comp);
|
|
|
+}
|
|
|
+
|
|
|
+static int stbi_hdr_info(stbi *s, int *x, int *y, int *comp)
|
|
|
+{
|
|
|
+ char buffer[HDR_BUFLEN];
|
|
|
+ char *token;
|
|
|
+ int valid = 0;
|
|
|
+
|
|
|
+ if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) {
|
|
|
+ stbi_rewind( s );
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ for(;;) {
|
|
|
+ token = hdr_gettoken(s,buffer);
|
|
|
+ if (token[0] == 0) break;
|
|
|
+ if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!valid) {
|
|
|
+ stbi_rewind( s );
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ token = hdr_gettoken(s,buffer);
|
|
|
+ if (strncmp(token, "-Y ", 3)) {
|
|
|
+ stbi_rewind( s );
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ token += 3;
|
|
|
+ *y = (int) strtol(token, &token, 10);
|
|
|
+ while (*token == ' ') ++token;
|
|
|
+ if (strncmp(token, "+X ", 3)) {
|
|
|
+ stbi_rewind( s );
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ token += 3;
|
|
|
+ *x = (int) strtol(token, NULL, 10);
|
|
|
+ *comp = 3;
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+#endif // STBI_NO_HDR
|
|
|
+
|
|
|
+static int stbi_bmp_info(stbi *s, int *x, int *y, int *comp)
|
|
|
+{
|
|
|
+ int hsz;
|
|
|
+ if (get8(s) != 'B' || get8(s) != 'M') {
|
|
|
+ stbi_rewind( s );
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ skip(s,12);
|
|
|
+ hsz = get32le(s);
|
|
|
+ if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) {
|
|
|
+ stbi_rewind( s );
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ if (hsz == 12) {
|
|
|
+ *x = get16le(s);
|
|
|
+ *y = get16le(s);
|
|
|
+ } else {
|
|
|
+ *x = get32le(s);
|
|
|
+ *y = get32le(s);
|
|
|
+ }
|
|
|
+ if (get16le(s) != 1) {
|
|
|
+ stbi_rewind( s );
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ *comp = get16le(s) / 8;
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+static int stbi_info_main(stbi *s, int *x, int *y, int *comp)
|
|
|
+{
|
|
|
+ if (stbi_bmp_info(s, x, y, comp))
|
|
|
+ return 1;
|
|
|
+ #ifndef STBI_NO_HDR
|
|
|
+ if (stbi_hdr_info(s, x, y, comp))
|
|
|
+ return 1;
|
|
|
+ #endif
|
|
|
+ // test tga last because it's a crappy test!
|
|
|
+ if (stbi_tga_info(s, x, y, comp))
|
|
|
+ return 1;
|
|
|
+ return e("unknown image type", "Image not of any known type, or corrupt");
|
|
|
+}
|
|
|
+
|
|
|
+#ifndef STBI_NO_STDIO
|
|
|
+int stbi_info(char const *filename, int *x, int *y, int *comp)
|
|
|
+{
|
|
|
+ FILE *f = fopen(filename, "rb");
|
|
|
+ int result;
|
|
|
+ if (!f) return e("can't fopen", "Unable to open file");
|
|
|
+ result = stbi_info_from_file(f, x, y, comp);
|
|
|
+ fclose(f);
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+int stbi_info_from_file(FILE *f, int *x, int *y, int *comp)
|
|
|
+{
|
|
|
+ int r;
|
|
|
+ stbi s;
|
|
|
+ long pos = ftell(f);
|
|
|
+ start_file(&s, f);
|
|
|
+ r = stbi_info_main(&s,x,y,comp);
|
|
|
+ fseek(f,pos,SEEK_SET);
|
|
|
+ return r;
|
|
|
+}
|
|
|
+#endif // !STBI_NO_STDIO
|
|
|
+
|
|
|
+int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp)
|
|
|
+{
|
|
|
+ stbi s;
|
|
|
+ start_mem(&s,buffer,len);
|
|
|
+ return stbi_info_main(&s,x,y,comp);
|
|
|
+}
|
|
|
+
|
|
|
+int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp)
|
|
|
+{
|
|
|
+ stbi s;
|
|
|
+ start_callbacks(&s, (stbi_io_callbacks *) c, user);
|
|
|
+ return stbi_info_main(&s,x,y,comp);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ revision history:
|
|
|
+ 1.35 (2014-05-27)
|
|
|
+ various warnings
|
|
|
+ fix broken STBI_SIMD path
|
|
|
+ fix bug where stbi_load_from_file no longer left file pointer in correct place
|
|
|
+ fix broken non-easy path for 32-bit BMP (possibly never used)
|
|
|
+ TGA optimization by Arseny Kapoulkine
|
|
|
+ 1.34 (unknown)
|
|
|
+ use STBI_NOTUSED in resample_row_generic(), fix one more leak in tga failure case
|
|
|
+ 1.33 (2011-07-14)
|
|
|
+ make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements
|
|
|
+ 1.32 (2011-07-13)
|
|
|
+ support for "info" function for all supported filetypes (SpartanJ)
|
|
|
+ 1.31 (2011-06-20)
|
|
|
+ a few more leak fixes, bug in PNG handling (SpartanJ)
|
|
|
+ 1.30 (2011-06-11)
|
|
|
+ added ability to load files via callbacks to accomidate custom input streams (Ben Wenger)
|
|
|
+ removed deprecated format-specific test/load functions
|
|
|
+ removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway
|
|
|
+ error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha)
|
|
|
+ fix inefficiency in decoding 32-bit BMP (David Woo)
|
|
|
+ 1.29 (2010-08-16)
|
|
|
+ various warning fixes from Aurelien Pocheville
|
|
|
+ 1.28 (2010-08-01)
|
|
|
+ fix bug in GIF palette transparency (SpartanJ)
|
|
|
+ 1.27 (2010-08-01)
|
|
|
+ cast-to-stbi__uint8 to fix warnings
|
|
|
+ 1.26 (2010-07-24)
|
|
|
+ fix bug in file buffering for PNG reported by SpartanJ
|
|
|
+ 1.25 (2010-07-17)
|
|
|
+ refix trans_data warning (Won Chun)
|
|
|
+ 1.24 (2010-07-12)
|
|
|
+ perf improvements reading from files on platforms with lock-heavy fgetc()
|
|
|
+ minor perf improvements for jpeg
|
|
|
+ deprecated type-specific functions so we'll get feedback if they're needed
|
|
|
+ attempt to fix trans_data warning (Won Chun)
|
|
|
+ 1.23 fixed bug in iPhone support
|
|
|
+ 1.22 (2010-07-10)
|
|
|
+ removed image *writing* support
|
|
|
+ stbi_info support from Jetro Lauha
|
|
|
+ GIF support from Jean-Marc Lienher
|
|
|
+ iPhone PNG-extensions from James Brown
|
|
|
+ warning-fixes from Nicolas Schulz and Janez Zemva (i.e. Janez (U+017D)emva)
|
|
|
+ 1.21 fix use of 'stbi__uint8' in header (reported by jon blow)
|
|
|
+ 1.20 added support for Softimage PIC, by Tom Seddon
|
|
|
+ 1.19 bug in interlaced PNG corruption check (found by ryg)
|
|
|
+ 1.18 2008-08-02
|
|
|
+ fix a threading bug (local mutable static)
|
|
|
+ 1.17 support interlaced PNG
|
|
|
+ 1.16 major bugfix - convert_format converted one too many pixels
|
|
|
+ 1.15 initialize some fields for thread safety
|
|
|
+ 1.14 fix threadsafe conversion bug
|
|
|
+ header-file-only version (#define STBI_HEADER_FILE_ONLY before including)
|
|
|
+ 1.13 threadsafe
|
|
|
+ 1.12 const qualifiers in the API
|
|
|
+ 1.11 Support installable IDCT, colorspace conversion routines
|
|
|
+ 1.10 Fixes for 64-bit (don't use "unsigned long")
|
|
|
+ optimized upsampling by Fabian "ryg" Giesen
|
|
|
+ 1.09 Fix format-conversion for PSD code (bad global variables!)
|
|
|
+ 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz
|
|
|
+ 1.07 attempt to fix C++ warning/errors again
|
|
|
+ 1.06 attempt to fix C++ warning/errors again
|
|
|
+ 1.05 fix TGA loading to return correct *comp and use good luminance calc
|
|
|
+ 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free
|
|
|
+ 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR
|
|
|
+ 1.02 support for (subset of) HDR files, float interface for preferred access to them
|
|
|
+ 1.01 fix bug: possible bug in handling right-side up bmps... not sure
|
|
|
+ fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all
|
|
|
+ 1.00 interface to zlib that skips zlib header
|
|
|
+ 0.99 correct handling of alpha in palette
|
|
|
+ 0.98 TGA loader by lonesock; dynamically add loaders (untested)
|
|
|
+ 0.97 jpeg errors on too large a file; also catch another malloc failure
|
|
|
+ 0.96 fix detection of invalid v value - particleman@mollyrocket forum
|
|
|
+ 0.95 during header scan, seek to markers in case of padding
|
|
|
+ 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same
|
|
|
+ 0.93 handle jpegtran output; verbose errors
|
|
|
+ 0.92 read 4,8,16,24,32-bit BMP files of several formats
|
|
|
+ 0.91 output 24-bit Windows 3.0 BMP files
|
|
|
+ 0.90 fix a few more warnings; bump version number to approach 1.0
|
|
|
+ 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd
|
|
|
+ 0.60 fix compiling as c++
|
|
|
+ 0.59 fix warnings: merge Dave Moore's -Wall fixes
|
|
|
+ 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian
|
|
|
+ 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available
|
|
|
+ 0.56 fix bug: zlib uncompressed mode len vs. nlen
|
|
|
+ 0.55 fix bug: restart_interval not initialized to 0
|
|
|
+ 0.54 allow NULL for 'int *comp'
|
|
|
+ 0.53 fix bug in png 3->4; speedup png decoding
|
|
|
+ 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments
|
|
|
+ 0.51 obey req_comp requests, 1-component jpegs return as 1-component,
|
|
|
+ on 'test' only check type, not whether we support this variant
|
|
|
+ 0.50 first released version
|
|
|
+*/
|