diff --git a/imv.c b/imv.c index 0339426..e1fb036 100644 --- a/imv.c +++ b/imv.c @@ -11,7 +11,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU General Public License star along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -29,6 +29,8 @@ #include #include #include +#include +#include #define STB_DEFINE #include "stb.h" /* http://nothings.org/stb.h */ @@ -252,26 +254,6 @@ typedef struct // there can only be one pending command in flight volatile DiskCommand dc_shared; -void *stb_file2(char *filename, size_t *length) -{ - FILE *f = fopen(filename, "rb"); - char *buffer; - size_t len; - if (!f) return NULL; - len = stb_filelen(f); - buffer = (char *) malloc(len+2); // nul + extra - if (fread(buffer, 1, len, f) == len) { - if (length) *length = len; - buffer[len] = 0; - } else { - free(buffer); - buffer = NULL; - } - fclose(f); - return buffer; -} - - // the disk loader sits in this loop forever void *diskload_task(void *p) { @@ -367,10 +349,11 @@ static unsigned char alpha_background[2][3] = // windows-compatible bitmap with 4-byte aligned rows and BGR color order) #if ALLOW_RECOLORING float lmin=0,lmax=1; +int mono; #endif void make_image(Image *z, int image_x, int image_y, uint8 *image_data, BOOL image_loaded_as_rgb, int image_n) { - int i,j,k=0; + int i,j,k,ymin=0,ymax=256*8-1; z->pixels = image_data; z->x = image_x; z->y = image_y; @@ -378,6 +361,21 @@ void make_image(Image *z, int image_x, int image_y, uint8 *image_data, BOOL imag z->frame = 0; z->had_alpha = (image_n==4); + #if ALLOW_RECOLORING + if (mono) { + k = 0; + for (j=0; j < image_y; ++j) { + for (i=0; i < image_x; ++i) { + int y = image_data[k+0]*5 + image_data[k+1]*9 + image_data[k+2]*2; + if (y < ymin) ymin = y; + if (y > ymax) ymax = y; + k += BPP; + } + } + } + #endif + + k=0; for (j=0; j < image_y; ++j) { for (i=0; i < image_x; ++i) { // TODO: hoist branches outside of loops? @@ -389,7 +387,13 @@ void make_image(Image *z, int image_x, int image_y, uint8 *image_data, BOOL imag } #if ALLOW_RECOLORING - if (lmin > 0 || lmax < 1) { + if (mono) { + int y = image_data[k+0]*5 + image_data[k+1]*9 + image_data[k+2]*2; + int p = (int) stb_linear_remap(y, ymin, ymax, 0,255); + image_data[k+0] = p; + image_data[k+1] = p; + image_data[k+2] = p; + } else if (lmin > 0 || lmax < 1) { int c; for (c=0; c < 3; ++c) { int z = (int) stb_linear_remap(image_data[k+c], lmin*255,lmax*255, 0,255); @@ -477,7 +481,7 @@ start: return best; } -static uint8 *imv_decode_from_memory(uint8 *mem, int len, int *x, int *y, BOOL *loaded_as_rgb, int *n, int n_req); +static uint8 *imv_decode_from_memory(uint8 *mem, int len, int *x, int *y, BOOL *loaded_as_rgb, int *n, int n_req, char *filename); static char *imv_failure_reason(void); void *decode_task(void *p) @@ -499,7 +503,7 @@ void *decode_task(void *p) // decode image o(("DECIDE: decoding %s\n", f->filename)); - data = imv_decode_from_memory(f->filedata, f->len, &x, &y, &loaded_as_rgb, &n, BPP); + data = imv_decode_from_memory(f->filedata, f->len, &x, &y, &loaded_as_rgb, &n, BPP, f->filename); o(("DECODE: decoded %s\n", f->filename)); // free copy of data from disk, which we don't need anymore @@ -604,7 +608,7 @@ Image *cur; // the filename for the currently displayed image char *cur_filename; int show_help=0; -int downsample_cubic = 0; +int downsample_cubic = TRUE; int upsample_cubic = TRUE; int cur_loc = -1; // offset within the current list of files @@ -706,8 +710,10 @@ void build_label_font(void) label_font = CreateFontIndirect(&lf); } +char path_to_file[4096]; int show_frame = TRUE; // show border or not? int show_label = FALSE; // display the help text or not +int recursive = FALSE; // WM_PAINT, etc. void display(HWND win, HDC hdc) @@ -760,7 +766,10 @@ void display(HWND win, HDC hdc) char buffer[1024]; char *name = cur_filename ? cur_filename : "(none)"; if (fileinfo) { - sprintf(buffer, "%s ( %d / %d )", name, cur_loc+1, stb_arr_len(fileinfo)); + if (recursive) + sprintf(buffer, "%s ( %d / %d - %s)", name, cur_loc+1, stb_arr_len(fileinfo), path_to_file); + else + sprintf(buffer, "%s ( %d / %d )", name, cur_loc+1, stb_arr_len(fileinfo)); name = buffer; } @@ -1154,7 +1163,6 @@ void toggle_display(void) // if you switch directories, the cache will still have // images from the old directory, and if you switch back // before they're flushed, it will still be valid -char path_to_file[4096]; char *filename; // @TODO: gah, we have cur_filename AND filename. and filename is being set dumbly! // stb_sdict is a string dictionary (strings as keys, void * as values) @@ -1270,7 +1278,7 @@ int StringCompareSort(const void *p, const void *q) return StringCompare(*(char **) p, *(char **) q); } -char *open_filter = "Image Files\0*.jpg;*.jpeg;*.png;*.bmp;*.tga;*.hdr\0"; +char *open_filter = "Image Files\0*.jpg;*.jpeg;*.png;*.bmp;*.tga;*.hdr;*.spk\0"; // build a filelist for the current directory void init_filelist(void) @@ -1285,7 +1293,11 @@ void init_filelist(void) free_fileinfo(); } - image_files = stb_readdir_files_mask(path_to_file, open_filter + 12); + if (recursive) + image_files = stb_readdir_recursive(path_to_file, open_filter + 12); + else + image_files = stb_readdir_files_mask(path_to_file, open_filter + 12); + if (image_files == NULL) { error("Error: couldn't read directory."); exit(0); } qsort(image_files, stb_arr_len(image_files), sizeof(*image_files), StringCompareSort); @@ -1546,15 +1558,34 @@ void advance(int dir) // with 'advance' static char filenamebuffer[4096]; +void stb_from_utf8_multi(stb__wchar *out, char *in, int max_out) +{ + // an array of \0 strings terminated by \0\0 + for(;;) { + int nout; + stb_from_utf8(out, in, max_out); + nout = wcslen(out)+1; // number of output characters consumed + max_out -= nout; + out += nout; + if (in[0] == 0) return; + in += strlen(in)+1; + } +} + void open_file(void) { - OPENFILENAME o = { sizeof(o) }; - o.lpstrFilter = open_filter; - o.lpstrFile = filenamebuffer; - filenamebuffer[0] = 0; - o.nMaxFile = sizeof(filenamebuffer); - if (!GetOpenFileName(&o)) + stb__wchar buf1[1024], buf2[4096]; + OPENFILENAMEW o = { sizeof(o) }; + + stb_from_utf8_multi(buf1, open_filter, sizeof(buf1)); + o.lpstrFilter = buf1; + o.lpstrFile = buf2; + buf2[0] = 0; + o.nMaxFile = sizeof(buf2); + o.Flags = OFN_HIDEREADONLY; + if (!GetOpenFileNameW(&o)) return; + stb_to_utf8(filenamebuffer, buf2, sizeof(filenamebuffer)); filename = filenamebuffer; stb_fixpath(filename); stb_splitpath(path_to_file, filename, STB_PATH); @@ -1818,7 +1849,7 @@ int reg_set(char *str, void *data, int len) return (ERROR_SUCCESS == RegSetValueEx(zreg, str, 0, REG_BINARY, data, len)); } -#ifdef USE_STBI +#if USE_STBI int only_stbi=FALSE; #endif @@ -2049,7 +2080,7 @@ BOOL CALLBACK PrefDlgProc(HWND hdlg, UINT imsg, WPARAM wparam, LPARAM lparam) show_label = BST_CHECKED == SendMessage(GetDlgItem(hdlg,DIALOG_showlabel), BM_GETCHECK,0,0); #if USE_STBI only_stbi = BST_CHECKED == SendMessage(GetDlgItem(hdlg,DIALOG_stbi_only), BM_GETCHECK,0,0); -#endif USE_STBI +#endif //USE_STBI new_border = BST_CHECKED == SendMessage(GetDlgItem(hdlg,DIALOG_showborder),BM_GETCHECK,0,0); // if alpha_background changed, clear the cache of any images that used it @@ -2102,14 +2133,14 @@ void performance_test(void) { int t1,t2; int len,i; - uint8 *buffer = stb_file2(cur_filename, &len); + uint8 *buffer = stb_file(cur_filename, &len); if (buffer == NULL) return; t1 = timeGetTime(); for (i=0; i < 50; ++i) { int x,y,n; - uint8 *result = imv_decode_from_memory(buffer, len, &x, &y, &n, 4); + uint8 *result = imv_decode_from_memory(buffer, len, &x, &y, &n, 4, cur_filename); free(result); } @@ -2134,6 +2165,15 @@ void performance_test(void) #define VK_SLASH 0xbf #endif +#ifndef WM_APPCOMMAND +#define WM_APPCOMMAND 0x0319 +#define APPCOMMAND_BROWSER_BACKWARD 1 +#define APPCOMMAND_BROWSER_FORWARD 2 +#define APPCOMMAND_OPEN 30 +#define FAPPCOMMAND_MASK 0xF000 +#define GET_APPCOMMAND_LPARAM(lParam) ((short)(HIWORD(lParam) & ~FAPPCOMMAND_MASK)) +#endif + // ok, surely windows doesn't BY DESIGN require you to store your // HINSTANCE in a global, does it? but I couldn't find a 'GetCurrentInstance' // or some such to tell you what instance a thread came from. But the @@ -2156,7 +2196,8 @@ int WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) // whether to show it or not; we do that by scanning the whole cache // to see what the most recently-browsed-and-displayable image is, // and store that in 'best'. - int best_lru=0,i; + int i; + // int best_lru=0; volatile ImageFile *best = NULL; for (i=0; i < MAX_CACHED_IMAGES; ++i) { if (cache[i].lru > best_lru) { @@ -2169,7 +2210,7 @@ int WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } } // if the most recently-browsed and displayable image is an error, show it - if (best->status == LOAD_error_reading || best->status == LOAD_error_decoding) + if (best && (best->status == LOAD_error_reading || best->status == LOAD_error_decoding)) set_error(best); break; @@ -2243,6 +2284,23 @@ int WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) return 0; } + case WM_APPCOMMAND: { + switch (GET_APPCOMMAND_LPARAM(lParam)) { + case APPCOMMAND_BROWSER_FORWARD: + advance(1); + break; + case APPCOMMAND_BROWSER_BACKWARD: + advance(-1); + break; + case APPCOMMAND_OPEN: + open_file(); + break; + default: + return DefWindowProc (hWnd, uMsg, wParam, lParam); + } + break; + } + #define MY_SHIFT (1 << 16) #define MY_CTRL (1 << 17) #define MY_ALT (1 << 18) @@ -2281,11 +2339,23 @@ int WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) KillTimer(win,0); break; + case '.': { + char buffer[512]; + strcpy(buffer, path_to_file); + if (buffer[strlen(buffer)-1] == '/') + buffer[strlen(buffer)-1] = 0; + stb_splitpath(path_to_file, buffer, STB_PATH); + if (recursive) + init_filelist(); + break; + } + #if ALLOW_RECOLORING case '[': lmax = stb_clamp(lmax-1.0f/32, 0,1); clear_cache(0); advance(0); break; case ']': lmax = stb_clamp(lmax+1.0f/32, 0,1); clear_cache(0); advance(0); break; case '{': lmin = stb_clamp(lmin-1.0f/32, 0,1); clear_cache(0); advance(0); break; case '}': lmin = stb_clamp(lmin+1.0f/32, 0,1); clear_cache(0); advance(0); break; + case 'm': mono = !mono; clear_cache(0); advance(0); break; #endif default: @@ -2364,6 +2434,34 @@ int WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) break; } + case 'R' | MY_CTRL: { + recursive = !recursive; + init_filelist(); + break; + } + + case 'R' | MY_ALT: { + static int init; + int n; + char buffer[512], **subdir; + recursive = TRUE; + strcpy(buffer, path_to_file); + if (buffer[strlen(buffer)-1] == '/') + buffer[strlen(buffer)-1] = 0; + stb_splitpath(path_to_file, buffer, STB_PATH); + subdir = stb_readdir_subdirs(path_to_file); + if (!init) { + init = 1; + stb_srand(time(NULL)); + } + n = stb_rand() % stb_arr_len(subdir); + strcpy(path_to_file, subdir[n]); + stb_readdir_free(subdir); + init_filelist(); + advance(0); + break; + } + case 'I' | MY_CTRL: { // not sure which of these is smaller #if 0 @@ -2458,8 +2556,11 @@ static int LoadFreeImage(void); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { + LPWSTR cmdline = GetCommandLineW(); int argc; - char **argv = stb_tokens_quoted(lpCmdLine, " ", &argc); + LPWSTR *argv = CommandLineToArgvW(cmdline, &argc); + //int argc; + //char **argv = stb_tokens_quoted(lpCmdLine, " ", &argc); char filenamebuffer[4096]; MEMORYSTATUS mem; @@ -2467,6 +2568,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine WNDCLASSEX wndclass = { sizeof(wndclass) }; HWND hWnd; + // initial loaded image int image_x, image_y, image_loaded_as_rgb, image_n; unsigned char *image_data; @@ -2493,9 +2595,9 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine #if USE_GDIPLUS if (LoadGdiplus()) { strcat(helptext_center, "\nUsing GDI+"); - open_filter = "Image Files\0*.jpg;*.jpeg;*.png;*.bmp;*.tga;" + open_filter = "Image Files\0*.jpg;*.jpeg;*.png;*.bmp;*.tga" #if USE_STBI - "*.hdr;" + "*.hdr;*.spk;" #endif "*.gif;*.ico;*.jng;*.tiff\0"; } @@ -2508,7 +2610,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine strcat(helptext_center, "\nUsing FreeImage.dll: http://freeimage.sourceforge.net"); open_filter = "Image Files\0*.jpg;*.jpeg;*.png;*.bmp;*.tga;" #if USE_STBI - "*.hdr;" + "*.hdr;*.spk;" #endif "*.dds;*.gif;*.ico;*.jng;*.lbm;*.pcx;*.ppm;*.psd;*.tiff\0"; } @@ -2539,20 +2641,37 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine srand(time(NULL)); - if (argc < 1) { - // if run with no arguments, get an initial filename - OPENFILENAME o = { sizeof(o) }; - o.lpstrFilter = open_filter; - o.lpstrFile = filenamebuffer; - filenamebuffer[0] = 0; - o.nMaxFile = sizeof(filenamebuffer); - if (!GetOpenFileName(&o)) + if (argc < 2) { + stb__wchar buf1[1024], buf2[4096]; + OPENFILENAMEW o = { sizeof(o) }; + + stb_from_utf8_multi(buf1, open_filter, sizeof(buf1)); + o.lpstrFilter = buf1; + o.lpstrFile = buf2; + buf2[0] = 0; + o.nMaxFile = sizeof(buf2); + if (!GetOpenFileNameW(&o)) return 0; + stb_to_utf8(filenamebuffer, buf2, sizeof(filenamebuffer)); filename = filenamebuffer; } else { - // else grab the first one... what about additional names? should - // we launch more windows? or initialize the filelist to them, I guess? - filename = argv[0]; + DWORD p = GetFileAttributesW(argv[1]); + if (p != 0xffffffff && (p & FILE_ATTRIBUTE_DIRECTORY)) { + filename = ""; + recursive = 1; + stb_to_utf8(path_to_file, argv[1], sizeof(path_to_file)); + init_filelist(); + if (stb_arr_len(fileinfo)) + filename = fileinfo[0].filename; + else { + error("No image files in folder."); + } + } else { + // else grab the first one... what about additional names? should + // we launch more windows? or initialize the filelist to them, I guess? + stb_to_utf8(filenamebuffer, argv[1], sizeof(filenamebuffer)); + filename = filenamebuffer; + } } // allocate worker threads @@ -2562,11 +2681,11 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine { char *why=NULL; int len; - uint8 *data = stb_file2(filename, &len); + uint8 *data = stb_file(filename, &len); if (!data) why = "Couldn't open file"; else { - image_data = imv_decode_from_memory(data, len, &image_x, &image_y, &image_loaded_as_rgb, &image_n, BPP); + image_data = imv_decode_from_memory(data, len, &image_x, &image_y, &image_loaded_as_rgb, &image_n, BPP, filename); if (image_data == NULL) why = imv_failure_reason(); } @@ -2955,6 +3074,7 @@ static Color lerp(Color dest, Color src, uint8 a) // out = (a*t+b)*t^2 + (c*t+d)*1 MMX int16 three[4] = { 3,3,3,3 }; +MMX int16 round[4] = { 128,128,128,128 }; static void cubic_interpolate_span(uint32 *dest, uint32 *x0, uint32 *x1, uint32 *x2, uint32 *x3, @@ -2982,6 +3102,7 @@ static void cubic_interpolate_span(uint32 *dest, punpcklbw mm7,mm7 // 0,0,0,0,lerp,lerp,lerp,lerp punpcklbw mm7,mm7 // 8xlerp. (This meakes each unsigned lerp value 0..15) psrlw mm7,1 // slide away from the sign bit; 1.15 lerp + // clearer way to thinkg of this: mm7 contains t/2 } looptop: __asm { movd mm1,[eax] @@ -2995,6 +3116,12 @@ static void cubic_interpolate_span(uint32 *dest, punpcklbw mm2,mm0 // mm2 = x1 punpcklbw mm3,mm0 // mm3 = x2 + // extra precision + psllw mm1,2 + psllw mm2,2 + psllw mm3,2 + psllw mm4,2 + add ecx,step_src add edx,step_src #if 1 @@ -3007,22 +3134,23 @@ static void cubic_interpolate_span(uint32 *dest, movq mm6,mm3 // mm6 = x2 psubw mm5,mm3 // mm5 = x1-x2 paddw mm3,mm1 // mm3 = x0+x2 - psubw mm6,mm1 // mm6 = c + psubw mm6,mm1 // mm6 = x2-x0 = c psubw mm3,mm2 // mm3 = x0+x2-d/2 pmullw mm5,three // mm5 = 3*(x1-x2) psubw mm3,mm2 // mm3 = x0+x2-d - pmulhw mm6,mm7 // mm6 = c*t + pmulhw mm6,mm7 // mm6 = c*t/2 paddw mm5,mm4 // mm5 = a psubw mm3,mm5 // mm3 = b - psllw mm5,2 // mm5 = a(15.1) - psllw mm3,1 // mm3 = b - pmulhw mm5,mm7 // mm5 = a*t - paddw mm6,mm2 // mm6 = c*t+d - paddw mm5,mm3 // mm5 = a*t + b + psllw mm5,2 // mm5 = a*4 + psllw mm3,1 // mm3 = b*2 + pmulhw mm5,mm7 // mm5 = a*t*2 + paddw mm6,mm2 // mm6 = (c*t+d)/2 + paddw mm5,mm3 // mm5 = (a*t + b)*2 pmulhw mm5,mm7 // mm5 = a*t^2+b*t - pmulhw mm5,mm7 // mm5 = a*t^3+b*t^2 + pmulhw mm5,mm7 // mm5 = (a*t^3+b*t^2)/2 paddw mm5,mm6 + psraw mm5,2 packuswb mm5,mm5 movd [edi],mm5 #else @@ -3738,7 +3866,7 @@ uint8 *LoadImageWithFreeImage(FIMEMORY *fi, int *x, int *y, int *n, int n_req) } #endif -static uint8 *imv_decode_from_memory(uint8 *mem, int len, int *x, int *y, Bool* loaded_as_rgb, int *n, int n_req) +static uint8 *imv_decode_from_memory(uint8 *mem, int len, int *x, int *y, Bool* loaded_as_rgb, int *n, int n_req, char *filename) { uint8 *res = NULL; imv_failure_string = NULL; @@ -3754,6 +3882,58 @@ static uint8 *imv_decode_from_memory(uint8 *mem, int len, int *x, int *y, Bool* } imv_failure_string = stbi_failure_reason(); + if ((mem[0] == 's' || mem[0] == 'x') && memcmp(mem+1, "PIC-delta-image", 16) == 0) { + char full_filename[1024]; + int len2; + uint8 *mem2; + FILE *f; + stb_splitpath(full_filename, filename, STB_PATH); + strcat(full_filename, mem+17+4); + f = fopen(full_filename, "rb"); + if (f && (len2 = stb_filelen(f), mem2 = malloc(len2)) != NULL) { + fread(mem2, 1, len2, f); + fclose(f); f = NULL; + res = stbi_load_from_memory(mem2, len2, x, y, n, n_req); + if (res) { + int i,offset,c; + *loaded_as_rgb = TRUE; + offset = 17; + offset += 4 + *(int *) (mem+offset); + if ( *x != *(int *) (mem+offset ) + || *y != *(int *) (mem+offset+4) ) + { + free(res); + free(mem2); + return NULL; + } + offset += 8; // skip x,y + c = *(int *) (mem+offset); + assert(c >= 1 && c <= 4); + offset += 4; + while (offset < len) { + int start = *(int *) (mem+offset); + int count = *(int *) (mem+offset+4); + assert(start < *x* *y); + assert(count <= *x * *y - start); + if (start < 0 || start >= *x * *y) + break; + if (start+count < start || start+count > *x * *y) + break; + offset += 8; + for (i=0; i < count; ++i) { + memcpy(res + start * n_req, mem+offset, c); + ++start; + offset += c; + } + } + free(mem2); + return res; + } + free(mem2); + } + if (f) fclose(f); + } + if (only_stbi) return res; #endif @@ -3779,5 +3959,6 @@ static uint8 *imv_decode_from_memory(uint8 *mem, int len, int *x, int *y, Bool* // we'll get the unknown-type message from stbi_failure_reason() } #endif + return res; } diff --git a/notes.txt b/notes.txt index c7f02a7..2e9a62b 100644 --- a/notes.txt +++ b/notes.txt @@ -1,4 +1,19 @@ + Version 0.991: Beta 12 + * bugfix: .spk file recursion issues, security + + Version 0.99: Beta 11 ( 2008-02-07 ) + * bugfix: further attempt to support BACK/FORWARD + + Version 0.98: Beta 10 ( 2008-01-31 ) + * bugfix: attempt to support BACK/FORWARD mouse buttons + * feature: custom .spk image-delta file format + * secret feature: recursive slideshows with clumsy UI using 'ctrl-R' and '.' + + Version 0.97: Beta 9 ( 2007-11-27 ) + * bugfix: if starting path is unicode don't blow up trying to read the directory + Version 0.96: Beta 8 ( 2007-10-20 ) + * feature: VC7 project files * feature: imv_light.exe - uses GDI+ only, not stb_image or FreeImage * feature: press 's' for a primitive slideshow of current directory * feature: HDR support in stb_image diff --git a/release.bat b/release.bat index d1e1186..a119e6e 100644 --- a/release.bat +++ b/release.bat @@ -13,14 +13,17 @@ rem # Make release directory mkdir ..\stb_imv copy vc6\release\stb_imv.exe ..\stb_imv\imv.exe -copy "vc6\light release\stb_imv_light.exe" ..\stb_imv\imv_light.exe +copy vc6\light_release\stb_imv_light.exe ..\stb_imv\imv_light.exe copy readme_binary.txt+notes.txt ..\stb_imv\readme.txt rem # Make tarball directory mkdir ..\stb_imv_src-%VERSION% mkdir ..\stb_imv_src-%VERSION%\vc6 +mkdir ..\stb_imv_src-%VERSION%\vc7 copy vc6\stb_imv.ds? ..\stb_imv_src-%VERSION%\vc6 +copy vc7\stb_imv.sln ..\stb_imv_src-%VERSION%\vc7 +copy vc7\stb_imv.vcproj ..\stb_imv_src-%VERSION%\vc7 copy *.* ..\stb_imv_src-%VERSION% rem # @@ -40,6 +43,7 @@ del stb_imv_src-%VERSION%.zip zip -r stb_imv_src-%VERSION%.zip stb_imv_src-%VERSION% del /s /q stb_imv_src-%VERSION%\* rmdir stb_imv_src-%VERSION%\vc6 +rmdir stb_imv_src-%VERSION%\vc7 rmdir stb_imv_src-%VERSION% rem # upload diff --git a/stb.h b/stb.h index c1aaddb..8a4cdb5 100644 --- a/stb.h +++ b/stb.h @@ -1,13 +1,17 @@ -/* stb-1.93 -- Sean's Tool Box -- public domain -- http://nothings.org/stb.h +/* stb-2.02 - Sean's Tool Box -- public domain -- http://nothings.org/stb.h no warranty is offered or implied; use this code at your own risk -This is a single header file with a bunch of useful utilities -for getting stuff done in C/C++. + This is a single header file with a bunch of useful utilities + for getting stuff done in C/C++. + + Email bug reports, feature requests, etc. to 'sean' at the same site. + + + Documentation: http://nothings.org/stb/stb_h.html + Unit tests: http://nothings.org/stb/stb.c -Bug reports, feature requests, etc. can be mailed to 'sean' at the above site. ============================================================================ - You MUST #define STB_DEFINE @@ -19,107 +23,21 @@ Bug reports, feature requests, etc. can be mailed to 'sean' at the above site. #include "stb.h" All other files should just #include "stb.h" without the #define. - - ============================================================================ +Version History -1. Features overview - a quick sketch of what you'll find -2. Manifest - other files of note -3. History - upgrading? here's what's changed -4. Documentation - one-line docs for each function - - - - -------------------------------- - - -1. Features overview: - - - - STRINGS - stb_strncpy, stb_strtok, etc. - stb_tolower, stb_skipwhite, stb_dupreplace - stb_tokens_* - - FILES - stb_fgets, stb_fgets_malloc - stb_fput_varlen, stb_fput_ranged - stb_readdir_* - stb_splitpath - stb_fopen, stb_fclose - stb_file, stb_stringfile, stb_filelen - stb_miniml_* - - LOGGING, ERRORS - stb(), stb_fatal() - - VECTOR<> TYPE IN C - stb_arr_* - - HASH VALUES - stb_hash - stb_crc32, stb_adler32, stb_sha1 - - DATA STRUCTURES - stb_sdict, stb_extra - stb_ps - - MATH & BIT OPERATIONS - stb_rand, stb_shuffle - stb_bitcount, stb_bitreverse, stb_is_pow2 - stb_lerp, stb_unlerp, stb_linear_remap - - SEARCHING - stb_regex_*, stb_matcher_*, stb_wildcard_* - - MEMORY MANAGEMENT - stb_malloc - STB_MALLOC_WRAPPER - stb_wrapper - - - - - Functions in stb.h fall into two categories: - - hack-oriented - app-oriented - - A hack-oriented function is probably not very robust. It's designed - to streamline the process of creating things like personal tools that - don't need robustness anyway. Hack-oriented functions may be designed - to be extremely convenient to use but not very efficient, or be - designed to be extremely efficient (since some tools need good - efficiency). - - An app-oriented function is robust and reasonably efficient. - - I haven't documented which is which; my point is that if you're - doing the latter, some care is required. For example, routines - which accept an output buffer but no length for that buffer - are obviously not robust. - - - -------------------------------- - - -2. Manifest - - stb.h (this file) -- the entire library: the only file you really need - stb.html (TODO) -- more complete documentation - stb.c -- unit tests for most of the library - - - -------------------------------- - - -3. Version History - + 2.02 remove integrated documentation + 2.01 integrate various fixes; stb_force_uniprocessor + 2.00 revised stb_dupe to use multiple hashes + 1.99 stb_charcmp + 1.98 stb_arr_deleten, stb_arr_insertn + 1.97 fix stb_newell_normal() + 1.96 stb_hash_number() + 1.95 hack stb__rec_max; clean up recursion code to use new functions 1.94 stb_dirtree; rename stb_extra to stb_ptrmap - 1.93 stb_sem_new() API cleanup (no blockflag--starts blocked; use 'extra') + 1.93 stb_sem_new() API cleanup (no blockflag-starts blocked; use 'extra') 1.92 stb_threadqueue--multi reader/writer queue, fixed size or resizeable 1.91 stb_bgio_* for reading disk asynchronously 1.90 stb_mutex uses CRITICAL_REGION; new stb_sync primitive for thread @@ -226,539 +144,15 @@ Bug reports, feature requests, etc. can be mailed to 'sean' at the above site. (stb_array), (stb_arena) Parenthesized items have since been removed. - - -------------------------------- - - -4. Documentation - - -Functions which take an output pointer parameter (e.g. for multiple return -values) always accept NULL for that parameter unless noted otherwise. - - - LEGEND / KEY - - Characters prefixed before a function description have particular meanings: - - - call free() on only the return value to clean up _all_ memory - (even for e.g. arrays of arrays, which are allocated all at once) - - % function is a macro that writes to its first argument, so that - must be an lvalue, and you should avoid side effects - - . function is a macro that uses its arguments multiple times, so - be careful about side effects - - @ data is cached for a passed-in string based on its pointer value, - so the string must be constant; passing a NULL constant string - clears all cached data - - retv in descriptions, shorthand for 'value returned by the function' - - -non-"stb_" prefixed items - int8 , uint8 -- 8-bit integer types - int16, uint16 -- 16-bit integer types - int32, uint32 -- 32-bit integer types - uchar, ushort, -- unsigned integer types - uint, ulong - M_PI -- PI (3.141592...) - deg2rad(a) -- convert degrees to radians - rad2deg(a) -- convert radians to degrees -. min(a,b) -- minimum; not defined if compiling C++ -. max(a,b) -- maximum; not defined if compiling C++ -. swap(TYPE,a,b) -. stb_clamp(x,a,b) -- constrain x to a<=x<=b - - #define STB_ONLY to suppress the definition of things in the above section - -memory checking - STB_MALLOC_WRAPPER -- track malloc()/free() __FILE__ & __LINE__ - stb_wrapper_listall(func) -- call func() with all outstanding allocations - stb_wrapper_dump(filename) -- dump outstanding allocations to file - stb_wrapper_malloc(p,sz,f,l) -- track allocation @ file:f line:l - stb_wrapper_free(p,f,l) -- track deallocation - stb_wrapper_realloc(old,new,newsz,f,l) -- track realloc - stb_wrapper_calloc(n,sz,f,l) -- track calloc (or just use wrapper_malloc!) - - If you _globally_ define STB_MALLOC_WRAPPER, then any file that includes - stb.h will have malloc/free/realloc/strdup tracked by stb.h using - __FILE__ and __LINE__. Don't define this if you don't want the bloat! - - Note that the tracker does not store data directly in the allocations; - instead it uses a separate data structure (whose malloc/frees are NOT - tracked). This allows the wrappers above to be used on custom allocators - without any invasiveness (and avoids corruption issues). - -errors and logging - void stb_fatal(char *format, ...) -- print error and exit - void stb_(char *format, ...) -- log to "stb.log" - void stb_log(int active) -- turn on/off stb_(); default on - void stb_log_fileline(int active) -- on/off logging file/line in DEBUG - void stb_log_name(char *name) -- use 'name' instead of "stb.log" - -memory - void stb_swap(p,q,size) -- swap non-overlapping blocks of memory - void * stb_copy(p,size) -- strdup() for memory - void stb_reverse(p,num,size) -- reverse array p - -C-strings - int stb_prefix_count(x,y) -- rval = length of matching prefix -. int stb_prefix(x,y) -- 0==strncmp that y is a prefix of x -. int stb_prefixi(x,y) -- 0==strnicmp that y is a prefix of x - int stb_suffix(x,y) -- check if y is a suffix of x - int stb_suffixi(x,y) -- check if y is a suffix of y - int stb_strncpy(x,y,n) -- strncpy with guaranteed termination - char * stb_substr(x,n) -- malloc() an n-char prefix of x -- char * stb_duplower(x) -- malloc() and lowercase a string - void stb_tolower(x) -- in-place lowercase a string - char * stb_strtok(x,y,del) -- copy y->x to any del, return new y -- char * stb_dupreplace(x,old,new) -- replace "old" with "new" in x - char * stb_strichr(x,y) -- strchr(x,y) case-insensitive - char * stb_stristr(x,y) -- strstr(x,y) case-insensitive - char * stb_stricmp(x,y) -- portable stricmp -@ int stb_ischar(c,s) -- is c in the constant string s? (fast) - -C-strings (stb-1.05) - char * stb_skipwhite(str) -- return ptr into str past whitespace -- char ** stb_tokens(str,del,int *n) -- make array of tokens (*n = count) -- char ** stb_tokens_allowempty(...) -- as above, can have 0-length tokens -- char ** stb_tokens_stripwhite(...) -- tokens lack leading&trailing space -- char ** stb_tokens_quoted(...) -- "quote" delimiters and l&t space - -C-strings (stb-1.15) - void stb_fixpath(path) -- turn all \ into / - char * stb_strchr2(str,x,y) -- return ptr to first x or y - char * stb_strrchr2(str,x,y) -- return ptr to last x or y - void stb_replacedir(buf,f,path) -- replace existing dir in file with path - void stb_splitpath(buf,path,f) -- copy component of path into buf: - f = STB_PATH -- include path - f = STB_FILE -- include filename - f = STB_EXT -- include extension - f = STB_PATH_FILE - f = STB_FILE_EXT - -bit operations (stb-1.08) - int stb_bitcount(uint) -- number of 1 bits - uint32 stb_bitreverse(uint) -- bitwise reverse - uint stb_bitreverse8(uchar) -- bit reverse of 8 bits - uint stb_big32(uchar *) -- decode 32-bit big-endian int - uint stb_big16(uchar *) -- decode 16-bit big-endian int - uint stb_little32(uchar *) -- decode 32-bit little-endian int - uint stb_little16(uchar *) -- decode 16-bit little-endian int - int stb_is_pow2(uint n) -- is n a power of two? is_pow2(0)=1 - int stb_log2_ceil(uint n) -- _ceil (5)=_ceil (8)=3; _ceil (0)=-1 - int stb_log2_floor(uint n) -- _floor(4)=_floor(7)=2; _floor(0)=-1 - int stb_lowbit8(uint n) -- index of smallest 1 bit, or -1 - int stb_highbit8(uint n) -- index of highest 1 bit, or -1 - -math -. stb_lerp(x, a,b) -- lerp from a to b based on x=[0,1] -. stb_unlerp(y, a,b) -- compute x s.t. y=stb_lerp(x,a,b) -. stb_linear_remap(y, a,b,c,d) -- return z = [c..d] as y = [a..b] - - int stb_float_eq(x,y,thresh,ulps) -- true if x=y within thresh or ulps - -32-bit "checksums" (stb-1.10; sha-1 1.27) - uint32 stb_crc32(buf,len) -- CRC-32 checksum of buffer - uint32 stb_crc32_block(crc,buf,n) -- update crc32; init STB_CRC32_SEED - uint32 stb_adler32(ad32,buf,n) -- update adler32; STB_ADLER32_SEED - void stb_sha1(out,buf,len) -- output 20 byte digest for len-byte buf - int stb_sha1_file(out,filename)-- retval=1 if success, 0=can't open - -binary search helper - int stb_search_binary(s,min,max,flag) -- binary search, store in s, pass - min&max indices, retval = guess index - int stb_search_open(s,min,flag) -- as above, but open-ended on right - int stb_probe(s,c,&result) -- c<0,=0,>0 <=> goalguess - result=next guess, retval=keep going? - if flag is true, search finds smallest index equal to goal - if flag is false, search finds largest index equal to goal - - sample usage: - stb_search s; - int r = stb_search_binary(&s,0,100,1); - while (stb_probe(&s,strcmp(goal,str[r]),&r)); - if (!strcmp(goal,str[r])) ... - -hashing - uint stb_hash_fast(char *, uint)-- hash known-length, quality Hsieh hash - uint stb_hash(char *) -- hash value for string - uint stb_hashptr(void *) -- hash value of pointer itself - uint stb_rehash(uint) -- secondary hash value from raw hash - uint stb_rehash_improved(uint) -- better rehash - uint stb_hash2(char *,uint *) -- compute two separate 32-bit hashes - - stb_define_hash(TYPE,PREFIX,KEY,EMPTY,DEL,HASH,VALUE) - declares a hash table named TYPE, with functions named PREFIXwhatever, - with a key of type KEY, reserved key values EMPTY and DEL, hashing - code 'HASH' which given KEY k computes a hash value and 'return's it, - and the hash table producing values of type VALUE, where NULL is the - result for get that has no value. See code for more details. - - int stb_perfect_create(stb_perfect *,uint*,n) -- compute n-item perfect hash - retval=output table size - void stb_perfect_destroy(stb_perfect *) -- free perfect hash - int stb_perfect_hash(stb_perfect *, uint x) -- hash x; retval=-1 if not - - -string dictionary [ hash table from strings to pointers ] - - stb_sdict*stb_sdict_new(use_arena) -- dictionary map strings to void* - void stb_sdict_delete(dict,func)-- free sdict; func(v) for all values - void * stb_sdict_get(dict,str) -- return value for string - int stb_sdict_add(dict,str,p) -- add if !str present else 0 - void * stb_sdict_remove(dict,str) -- del if str present else 0 - void * stb_sdict_change(dict,str,p)-- replace old val with new, retv=old - char * stb_sdict_iter(dict,prev,&v)-- retv=next item; prev=NULL to start - -. stb_sdict_for(dict,int_var,str_var,p_var) {...} - -- iter over in dict - -extra-data dictiontary [ hash table from pointers to pointers ] - - stb_ptrmap *stb_ptrmap_new(void) -- dictionary from void* to void* - void stb_ptrmap_delete(map,func)--free dict; func(v) for all values - void * stb_ptrmap_get(dict,ptr) -- get value for pointer (NULL if none) - void stb_ptrmap_add(dict,p,v) -- bind key p1 to key p2 in dict - void stb_ptrmap_set(dict,p,v) -- set key p1 to key p2, add if needed - void * stb_ptrmap_remove(dict,p1)-- remove key p1, retval=value for p1 - -portable 32-bit random numbers via Mersenne Twister or LCG (BCPL generator) - void stb_srand(uint32 n) -- set seed - uint32 stb_rand() -- generate random number - double stb_frand() -- generate random number [0..1) -. stb_lerp(stb_frand(),x,y) -- generate random number [x..y) -. stb_rand_define(name,seed) -- defines random generator function NAME - void stb_shuffle(void*,n,sz,r) -- shuffle array length n, itemsize=sz - use 'r' as seed; don't disturb - random stream. Hint: use stb_rand() - or rand() as seed. - uint32 stb_srandLCG(uint32 n) -- set seed, returning old one - uint32 stb_randLCG() -- generate random number - double stb_frandLCG() -- generate random number [0..1) - -generic qsort routines - int stb_intcmp(a,b) -- qsort integer compare func - int stb_floatcmp(a,b) -- qsort float compare func - int stb_doublecmp(a,b) -- qsort double compare func - int stb_strcmp(a,b) -- qsort string compare func - void stb_cmpoffset(int n) -- set field offset within struct - - e.g. stb_cmpoffset(offsetof(ptype, float_field)); - qsort(p, n, sizeof(*p), stb_floatcmp); - stb_cmpoffset(0); // don't hose other code that doesn't set it - -templated quicksort -. stb_define_sort(FUNCNAME, TYPE, COMPARE_CODE) -- define a sort func - - This will define a sort function which has the name "FUNCNAME", and takes - two parameters: an array of TYPE, and an integer length for that array. - This allows inlining the comparison, which can perform better than - qsort(). For a simple test on a large array of ints, performance was - 2x qsort() in a VC6 release build, and roughly identical in a debug build. - - You need to define three things: - FUNCNAME -- the name of your sort function - TYPE -- the type of data it will sort; this must be copyable - COMPARE_CODE -- an expression that compares two TYPE * named a and b. - It should return TRUE if a and b are in sorted order - but NOT equal, and FALSE otherwise. - - If you need to do more complex comparisons, start your compare code with - "0;", and then compute your comparison into the variable 'c', e.g. - 0; c = *a < *b; - - To clarify, here are some rough equivalents: - - stb_declare_sort(FUNCNAME, TYPE): - void FUNCNAME(TYPE *p, int n); - - stb_define_sort(FUNCNAME, TYPE, COMPARE_CODE): - int compare_func(const void *p, const void *q) - { - TYPE *a = p; - TYPE *b = q; - if (COMPARE_CODE) - return -1; - else - return 0 or 1; - } - - void FUNCNAME(TYPE *p, int n) - { - qsort(p, n, sizeof(TYPE), compare_func); - } - - stb_define_sort_static is identical to stb_define_sort, but FUNCNAME - gets a storage specifier 'static'. - - -stb_dupe -- find duplicates in very large sets in O(N log log N) time - stb_dupe *stb_dupe_create(hash,eq,count,ineq) -- create dupe finder - hash = hash function on void* item - eq = equality comparison for two void* items - count = estimated number of items, or 0 - ineq = inequality comparison for two items, or NULL - void stb_dupe_free(dupe*) -- free dupe finder - void stb_dupe_add(dupe*, void *item) -- add item to dupe set - void stb_dupe_finish(dupe *) -- find dupes - int stb_dupe_numsets(dupe *) -- number of duplicate sets - void **stb_dupe_set(dupe*,index) -- list of dupes in set 'index',0..num-1 - int stb_dupe_set_count(dupe*,index) -- number of dupes in list - -options parsing - char ** stb_getopt(&argc,argv) -- retval=options; argc/argv modded - char ** stb_getopt_param(&c,v,plist)-- options in plist take one parameter - void stb_getopt_free(options) (note options is NULL terminated) - -directory reading (stb-1.23) - char ** stb_readdir_files(dir) -- return stb_arr of files in dir - char ** stb_readdir_subdirs(dir) -- return stb_arr of subdirs in dir - void stb_readdir_free(char**) -- free above - -file handling 3 (stb-1.24) - int stb_copyfile(char *src, char *dest) -- copy file src to dest - int stb_rename(char *src, char*dest) -- utf8 rename() - -file handling - char * stb_fgets(buf,len,FILE*) -- fgets with no trailing \n -- char * stb_fgets_malloc(FILE*) -- fgets arbitrarily long - size_t stb_filelen(FILE *) -- length of open file -- void * stb_file(name, int*n) -- read n-byte file into memory -- char ** stb_stringfile(name, *n) -- read n lines as strings -- char ** stb_stringfile_trimmed(name, int *n, comment_char) - -- strips leading whitespace; skips - empty lines and lines starting with comment char, - -file handling 2 [all functions work for both int and uint] - - void stb_fput_varlen(file,int) -- fwrite 1..5 bytes, small #s smaller - int stb_fget_varlen(f) -- fread 1..5 bytes as above - int stb_size_varlen(val) -- number of bytes required for value - void stb_fput_varlenu(file,uint)-- fwrite 1..5 bytes, small #s smaller - uint stb_fget_varlenu(f) -- fread 1..5 bytes as above - int stb_size_varlenu(val) -- number of bytes required for value - void stb_fput_ranged(f,val,b,n) -- write val, b <= val < b+n - int stb_fget_ranged(f,b,n) -- read val, b <= val < b+n - int stb_size_ranged(b,n) -- number of bytes required for range - void stb_fput_varlen64(file,i64)-- fwrite 1..9 bytes, small #s smaller - int64 stb_fget_varlen64(f) -- fread 1..9 bytes as above - int stb_size_varlen64(val64) -- number of bytes required for value - -full file processing - int stb_fcmp(char *x, char *y) -- compare files x <=> y, 0 if eq - int stb_feq(char *x, char *y) -- 'stb_fcmp()==0', faster - -no-overwrite file I/O [use tempfile and move on close] - - int stb_fullpath(abs,sz,fname) -- make fname not cwd-relative - char * stb_mktemp(char *template) -- if(Windows)_mktemp();else mkstemp() - FILE * stb_fopen(filename,mode) -- "w"/"wb": overwrite only on success - int stb_fclose(FILE *,keep) -- if keep & no error, save to file - keep == 0 == stb_keep_no => discard - keep == 1 == stb_keep_yes => keep - keep == 2 == stb_keep_if_different - -sliding-dict compression [English 2:1, stb.h 3:1, EXE 3:2] - int stb_decompress_length(cbuf,n) -- length of decompressed cbuf - int stb_decompress(dbuf,cbuf,n) -- decompress cbuf[0..n) to dbuf - int stb_compress(cbuf,dbuf,n) -- compress dbuf[0..n) to cbuf - void stb_compress_window(n) -- maximum distance to loook back - void stb_compress_hashsize(n) -- bytes to use for hash table - int stb_compress_tofile(name,dbuf,n) -- compress dbuf[0..n) > name -- char *stb_decompress_fromfile(name,&n) -- decompress from file 'name' - -stb_arr -- growable array [a la STL vector<>] - - Note that the pointer 'r' itself gets realloced/moved around on any - call marked with '%', so don't try to keep multiple pointers to the array. - If you pass one into a function, pass it by reference or return the - changed value out the other side. (Obviously could avoid this by wrapping - it inside another structure, but then it would be much more painful to - access an entry from C, which is the whole point of this particular API.) - - TYPE *r=NULL; -- declare 0-length array in C - stb_arr r=NULL -- declare 0-length array in C++ - stb_arr(TYPE) r -- declare 0-length array in either (e.g. func decl) - r[i] -- access i'th element of array - - void * stb_arr_free(r) -- free (returns NULL) e.g. a=stb_arr_free(a) -. int stb_arr_len(r) -- number of elements in array -. int stb_arr_empty(r) -- stb_arr_len(r)==0 -. int stb_arr_valid(r,i) -- test if i is valid index in array [0..len) -% TYPE stb_arr_push(r,v) -- append copyable item 'TYPE v' at end -. TYPE stb_arr_pop(r) -- remove item from end and return it -. TYPE stb_arr_last(r) -- LVALUE of last item, e.g. one just pushed -% TYPE * stb_arr_add(r) -- a gets 1 uninitialized item @end, ret=last -% TYPE * stb_arr_addn(r,n) -- a gets n uninitialized items @end, ret=1st -% TYPE * stb_arr_atleast(r,n) -- makes sure a has n "unallocated" elements -% TYPE * stb_arr_setlen(r,n) -- makes 'stb_arr_len(a) == n' true -% TYPE * stb_arr_makevalid(r,n)--makes 'stb_arr_valid(r,n)' true -% TYPE * stb_arr_setsize(r,n) -- [rare!] sets number of internal elements - void * stb_arr_copy(r) -- make a duplicate of the array - int stb_arr_storage(r) -- count #bytes to store array - -stb_ps -- pointer set -- a time/space efficient set of pointers - int stb_ps_find(ps,p) -- true if p is in ps - stb_ps *stb_ps_add(ps,p) -- put p (!= NULL) into ps - stb_ps *stb_ps_remove(ps,p) -- remove p from ps if present - stb_ps *stb_ps_remove_any(ps,&p) -- remove some element of ps and put in p - void stb_ps_delete(ps) -- discard the pointer set - int stb_ps_count(ps) -- # of pointers in pointer set - void ** stb_ps_getlist(ps,&c) -- malloc array of pointers from set - int stb_ps_writelist(ps,list,sz)--write as many as sz pointers to list - int stb_ps_enum(ps,cdata,f(p,cdata))--traverse all pointers in set - void ** stb_ps_fastlist(ps,&c) -- retval=list, c=length; list[i] is valid - iff stb_ps_fastlist_valid(list[i]) - - -XML-style parser, MiniML [XML subset for data description] - void stb_mml_free(stb_mml *z) -- free an MML tree - stb_mml *stb_mml_parse(char *s) -- parse string into MML tree - stb_mml *stb_mml_file(char *file) -- parse file into MML tree - - MiniML spec: stb_mml *m; - nested XML tags m->tag : string w. tag name - no options/attribs m->leaf_data : string w. tag contents IFF leaf - no style m->num_children : number of child stb_mml nodes - all content in leaves m->child[i] : i'th child -- [0,num_children) - optional for leaf - -stb_alloc -- hierarchical memory manager (stb_malloc(1) to get an arena) - - If you allocate a block q assigned to block p, then freeing p frees q. - Assigning to block NULL means it's global, it never auto-frees. - - void stb_free(p) -- free a malloc, _leaf; also assignees - void * stb_malloc(p,size) -- alloc a block q assigned to p - void * stb_malloc_nofree(p,sz) -- alloc q assigned to p; can't stb_free(q) - void * stb_malloc_leaf (p,sz) -- alloc q assigned to p; can't assign to q - void * stb_malloc_raw (p,sz) -- alloc q assigned to p; nofree & leaf - void * stb_malloc_global(sz) -- alloc q unassigned; stb_malloc(0,sz) - void * stb_realloc ( q,sz) -- realloc a malloc, _global, _leaf - void * stb_realloc_c(p,q,sz) -- malloc/free/realloc based on q=NULL,sz=0 - void stb_reassign(p,q) -- reassign q to p, remove old assignment - also only for malloc, _global, _leaf - - int stb_alloc_chunk_size; -- chunk size for _leaf, _raw, _string - int stb_alloc_alignment; -- alignment to force allocations to - -stb_match - - int stb_wildmatch(s,t) -- does wildcard s match t - int stb_wildmatchi(s,t) -- does wildcard s match t case insensitively - int stb_wildfind(s,t) -- is wildcard s anywhere in t - int stb_wildfindi(s,t) -- ditto, case insensitive - - stb_matcher *stb_regex_matcher(char *r) -- make regexp matching object - void stb_matcher_free(match *) -- free it - int stb_matcher_match(match *,str) -- does str match regexp? - int stb_matcher_find(match *,str) -- is regexp anywhere in str? - - Regexps: x x? x+ x* (x) foo|bar - . [a-z] []a-z] [^a-z] ^foo foo$ - \? \+ \* \( \) \[ \] - no {} - -@ int stb_regex(char *regex, char *str) -- regex search on _constant_ regex - (uses the regex pointer to cache a compiled regexp behind the scenes) - -stb_lex - stb_matcher * stb_lex_matcher() -- create a lexing parser - void stb_lex_item(matcher,re,val)-- define lexing regexp re to return val - int stb_lex(matcher,input,&len) -- find longest item (len=length), retv=val - -word wrapping - int stb_wordwrap(pairs, pair_max, count, *str) -- wrap str to width count - int *stb_wordwrapalloc(int count, char *str) -- as above, but alloc results - -stb_temp -- temporary storage on stack or not: - -% void * stb_temp(buffer, needed_size) -- reval = buffer or malloc if big - void stb_tempfree(buffer, p) -- p = retval of stb_temp() - -balanced binary search tree (uses AA tree, a 2-3 that's simpler than red-black) - -. stb_bst_fields(NAME) -- add to structure to be searched, - prefixed with 'struct ThisStructType' - - You can represent a BST index of this structure by using a pointer - to the structure (the tree pointer IS the structure pointer). But - then update functions will return new pointers, so wrap it if you - want. - - Note that if you have multiple fields in the structure you want - to index separately, you can do so by using different NAMEs. - -. stb_bst(TYPE,NAME,keyname,KEYTYPE,compare) - TYPE = structure name - NAME = name prefixed to BST functions - keyname = name of field in structure to index - KEYTYPE = type of field in structure to index - compare = _expression_ to compare two KEYTYPES a and b, then - compute <0,0,>0 for ab - -. stb_bst_general(TYPE,NAME,compare) -- allow multi-field comparisons; - TYPE = structure name does not define NAME##find - NAME = name prefixed to BST functions - compare = code to take two structure pointers p and q, then - invoke "return" with <0,0,>0 for pq - -. stb_bst_find(NAME,tree,fcomp) -- define find function for _general - TYPE *myfind(TYPE *mytree, myvariousfields) - stb_bst_find(NAME,mytree, ...fcomp-code...) - fcomp-code should compute <0, 0, >0 comparing myvariousfields - to mytree (e.g. < 0 iff myvariousfields < mytree) and store - the result in the variable 'c'. - - TYPE *NAME##insert(tree,item) -- retval=new tree - TYPE *NAME##remove(tree,item) -- retval=new tree - TYPE *NAME##first(tree) -- retval=first item in tree - TYPE *NAME##last(tree) -- retval=last item in tree - TYPE *NAME##next(tree,item) -- retval=next item after item - TYPE *NAME##prev(tree,item) -- retval=prev item after item - TYPE *NAME##find(tree,key) -- retval=item with key if exists - -image -- generic 32-bit image structure, fast alpha blend operations - - pixels are 'unsigned int' (or uint32, to be explicit) - -. stb_rgba(r,g,b,a) [each uint8] -- make a 32-bit pixel -. stb_rgb (r,g,b) -- 32-bit pixel, opaque -. stb_r(p), stb_g(p), stb_b(p), stb_a(p) -- get 8-bit channel values -. stb_image_opaque (p) -- is 32-bit pixel opaque? -. stb_image_transparent(p) -- is 32-bit pixel totally clear? -. stb_image_over (p,q) -- blend p over q (result opaque) -. stb_image_blend (p,q,uint8 a) -- blend p over q using alpha=a -. stb_image_blend_half(p,q) -- blend p over q using alpha=0.5 - - stb_image i; - - stb_image_define (&i,width,height,data) -- create, with pointer to data - stb_image_subimage(&i,&j,x,y,w,h) -- shares (x,y,w,h) block of j -. stb_image_pixel (&i,x,y) -- lvalue of (x,y)'th pixel - - -*****************************************************************************/ - -//@ -//@ This documentation is for stb.h version @@VERSION which consists of a -//@ single header file which should be located here. -//@ Generally the most recent version should be available on the internet at -//@ http://nothings.org/stb.h. -//@

Some infrequently used components have been removed and are -//@ available separately: -//@ variable-sized item storage in -//@ files and image and -//@ sampling. +*/ #ifndef STB__INCLUDE_STB_H #define STB__INCLUDE_STB_H #include // stdlib could have min/max #include // need FILE +#include // stb_define_hash needs memcpy/memset +#include // stb_dirtree #ifdef STB_PERSONAL typedef int Bool; @@ -785,7 +179,6 @@ image -- generic 32-bit image structure, fast alpha blend operations #ifdef STB_DEFINE #include - #include #include #include #include @@ -798,14 +191,6 @@ image -- generic 32-bit image structure, fast alpha blend operations #endif #include // stat()/_stat() #include // stat()/_stat() - - #ifdef _WIN32 - #define STB__INLINE static __forceinline - #elif defined(__cplusplus) - #define STB__INLINE static inline - #else - #define STB__INLINE static - #endif #endif #define stb_min(a,b) ((a) < (b) ? (a) : (b)) @@ -897,30 +282,14 @@ typedef char stb__testsize2_32[sizeof(stb_uint32)==4]; #include #endif -//@

stb_wrapper_*

-//@ -//@ record file/line information for tracking leaks

-//$ newp=pointer to a memory block that was just allocated -//$ oldp=pointer to a memory block that was previously allocated -//$ sz=size of the memory block just allocated -//$ file=name of the source file where the allocation operation occurred -//$ line=line number of the source file where the allocation operation occurred -//$ record an allocation made by a malloc()-like routine + STB_EXTERN void stb_wrapper_malloc(void *newp, int sz, char *file, int line); -//$ record a deallocation made by a free()-like routine STB_EXTERN void stb_wrapper_free(void *oldp, char *file, int line); -//$ record a reallocation made by a realloc()-like routine STB_EXTERN void stb_wrapper_realloc(void *oldp, void *newp, int sz, char *file, int line); STB_EXTERN void stb_wrapper_calloc(size_t num, size_t sz, char *file, int line); -//$ enumerate all the currently outstanding allocations that haven't been freed -//$ (callback function provided by the client which reports each allocated block and the file/line pair from the malloc/realloc that created it) STB_EXTERN void stb_wrapper_listall(void (*func)(void *ptr, int sz, char *file, int line)); -//$ write a text file with a listing of all currently outstanding allocations -//$ (name of the text file to create) STB_EXTERN void stb_wrapper_dump(char *filename); -//$ Return the allocation size of an allocated block, or 0 if not allocated. STB_EXTERN int stb_wrapper_allocsize(void *oldp); -//$ Check to make sure a block is really allocated STB_EXTERN void stb_wrapper_check(void *oldp); #ifdef STB_DEFINE @@ -940,13 +309,15 @@ static void * stb__realloc_raw(void *p, int sz) } #endif -#ifdef STB_FASTMALLOC +#ifdef _WIN32 STB_EXTERN void * stb_smalloc(size_t sz); STB_EXTERN void stb_sfree(void *p); STB_EXTERN void * stb_srealloc(void *p, size_t sz); STB_EXTERN void * stb_scalloc(size_t n, size_t sz); STB_EXTERN char * stb_sstrdup(char *s); +#endif +#ifdef STB_FASTMALLOC #define malloc stb_smalloc #define free stb_sfree #define realloc stb_srealloc @@ -1199,7 +570,7 @@ STB_EXTERN char * stb_sstrdup(char *s); strcpy(p, s); return p; } - #endif + #endif // STB_DEFINE #ifdef STB_FASTMALLOC #undef malloc @@ -1209,6 +580,11 @@ STB_EXTERN char * stb_sstrdup(char *s); #undef calloc #endif + // include everything that might define these, BEFORE making macros + #include + #include + #include + #define malloc(s) stb__malloc ( s, __FILE__, __LINE__) #define realloc(p,s) stb__realloc(p,s, __FILE__, __LINE__) #define calloc(n,s) stb__calloc (n,s, __FILE__, __LINE__) @@ -1351,37 +727,15 @@ char *stb__to_utf8(stb__wchar *str) // Miscellany // - -//@


Logging

-//$ Fatal error. Print an error message, trap into the debugger, and exit. -//$ fmt=string to print; supports `printf`-style formatting of additional parameters STB_EXTERN void stb_fatal(char *fmt, ...); -//$ Print a message to the logfile if it is enabled (defaults to enabled) STB_EXTERN void stb_(char *fmt, ...); -//$ Enable/disable the log file -//$ (if non-zero, logging will be enabled; if zero, logging will be disabled) STB_EXTERN void stb_log(int active); -//$ Enable/disable recording file & line information to the log file. -//$ This is only available in debug builds, or if STB_DEBUG is otherwise set. -//$ (if non-zero, file/line recording will be enabled; if zero, file/line recording will be disabled) STB_EXTERN void stb_log_fileline(int active); -//$ Set the filename to use logging with stb_()

Default is "stb.log" -//$ (Name of the file where future calls to stb_() will print.) STB_EXTERN void stb_log_name(char *filename); -//@


Miscellany

-//$ Swap two blocks of memory -//$ (pointer to first block of memory to swap|pointer to second block of memory to swap,size of both blocks in bytes) STB_EXTERN void stb_swap(void *p, void *q, size_t sz); -//$ malloc() a copy of a block of memory (like strdup, but for memory) -//$ returns NULL if malloc() returns NULL -//$ (pointer to the block of memory to copy|size of the the block of memory in bytes) STB_EXTERN void *stb_copy(void *p, size_t sz); -//$ Free all the pointers in an array -//$ (pointer to pointers to free|number of pointers to free) STB_EXTERN void stb_pointer_array_free(void **p, int len); -//$ Allocate an array of pointer to memory blocks -//$ (number of pointers top-level array|length of each block in bytes) STB_EXTERN void **stb_array_block_alloc(int count, int blocksize); #define stb_arrcount(x) (sizeof(x)/sizeof((x)[0])) @@ -1431,7 +785,11 @@ void stb_log_fileline(int active) stb__log_fileline = active; } +#ifdef STB_NO_STB_STRINGS +char *stb__log_filename = "temp.log"; +#else char *stb__log_filename = "stb.log"; +#endif void stb_log_name(char *s) { @@ -1534,14 +892,9 @@ void **stb_array_block_alloc(int count, int blocksize) // stb_temp // -//$ Allocate a block of memory either on the stack or from malloc. -//$ block=temporary variable on the stack (e.g. an array) -//$ (=; this is returned by stb_temp if it is large enough, otherwise memory is obtained with malloc()|the size of memory requested) #define stb_temp(block, sz) stb__temp(block, sizeof(block), (sz)) STB_EXTERN void * stb__temp(void *b, int b_sz, int want_sz); -//$ Deallocate the memory allocated by stb_temp() -//$ (= that was passed to stb_temp()|the pointer returned by stb_temp()) STB_EXTERN void stb_tempfree(void *block, void *ptr); #ifdef STB_DEFINE @@ -1571,9 +924,6 @@ void stb_tempfree(void *b, void *p) #define stb_lerp(t,a,b) ( (a) + (t) * (float) ((b)-(a)) ) #define stb_unlerp(t,a,b) ( ((t) - (a)) / (float) ((b) - (a)) ) -//$ Clamp a number so it lies within a specified range. -//$ Note this is a macro that evaluates arguments more than once. -//$ (value to clamp|minimum value of result|maximum value of result) #define stb_clamp(x,xmin,xmax) ((x) < (xmin) ? (xmin) : (x) > (xmax) ? (xmax) : (x)) STB_EXTERN void stb_newell_normal(float *normal, int num_vert, float **vert, int normalize); @@ -1595,9 +945,9 @@ void stb_newell_normal(float *normal, int num_vert, float **vert, int normalize) for (i=num_vert-1,j=0; j < num_vert; i=j++) { float *u = vert[i]; float *v = vert[j]; - normal[0] = (u[1] - v[1]) * (u[2] + v[2]); - normal[1] = (u[2] - v[2]) * (u[0] + v[0]); - normal[2] = (u[0] - v[0]) * (u[1] + v[1]); + normal[0] += (u[1] - v[1]) * (u[2] + v[2]); + normal[1] += (u[2] - v[2]) * (u[0] + v[0]); + normal[2] += (u[0] - v[0]) * (u[1] + v[1]); } if (normalize) { p = normal[0]*normal[0] + normal[1]*normal[1] + normal[2]*normal[2]; @@ -1664,7 +1014,7 @@ int stb_is_prime(unsigned int m) unsigned int stb_power_of_two_nearest_prime(int n) { - static unsigned int tab[32] = { 0,0,0,0,1,0,-1,0,1,-1,-1,3,-1,0,-1,2,1, + static signed char tab[32] = { 0,0,0,0,1,0,-1,0,1,-1,-1,3,-1,0,-1,2,1, 0,2,0,-1,-4,-1,5,-1,18,-2,15,2,-1,2,0 }; if (!tab[0]) { int i; @@ -1673,7 +1023,7 @@ unsigned int stb_power_of_two_nearest_prime(int n) tab[1] = 2; tab[0] = 1; } - if (n >= 32) return -5; // assumes 32-bit! + if (n >= 32) return 0xfffffffb; return tab[n]; } @@ -1805,14 +1155,10 @@ int stb_lowbit8(unsigned int n) // #ifdef _WIN32 - //$ case-insensitive comparison (portable equivalent to stricmp or strcasecmp) #define stb_stricmp(a,b) stricmp(a,b) - //$ case-insensitive comparison (portable equivalent to strnicmp or strncasecmp) #define stb_strnicmp(a,b,n) strnicmp(a,b,n) #else - //$ SKIP! #define stb_stricmp(a,b) strcasecmp(a,b) - //$ SKIP! #define stb_strnicmp(a,b,n) strncasecmp(a,b,n) #endif @@ -1822,6 +1168,7 @@ STB_EXTERN int stb_qsort_strcmp(const void *a, const void *b); STB_EXTERN int stb_qsort_stricmp(const void *a, const void *b); STB_EXTERN int stb_floatcmp(const void *a, const void *b); STB_EXTERN int stb_doublecmp(const void *a, const void *b); +STB_EXTERN int stb_charcmp(const void *a, const void *b); STB_EXTERN void stb_cmpoffset(int off); #ifdef STB_DEFINE @@ -1834,6 +1181,13 @@ int stb_intcmp(const void *a, const void *b) return p < q ? -1 : p > q; } +int stb_charcmp(const void *a, const void *b) +{ + const int p = *(const unsigned char *) ((const char *) a + stb__cmpoffset); + const int q = *(const unsigned char *) ((const char *) b + stb__cmpoffset); + return p < q ? -1 : p > q; +} + int stb_floatcmp(const void *a, const void *b) { const float p = *(const float *) ((const char *) a + stb__cmpoffset); @@ -1994,9 +1348,6 @@ int stb_search_open(stb_search *s, int minv, int find_smallest) // String Processing // -//$ returns true if the second string is a prefix of the first string, that -//$ is if the first string begins with the second string. -//$ (string to test|expected prefix) #define stb_prefixi(s,t) (0==stb_strnicmp((s),(t),strlen(t))) enum stb_splitpath_flag @@ -3210,17 +2561,6 @@ STB_EXTERN void stb_arr_malloc(void **target, void *context); // it turns the previous value, so you can restore it STB_EXTERN void* stb_arr_malloc_parent(void *p); -#ifdef STB_PERSONAL -#define arrpush stb_arr_push -#define arrlen stb_arr_len -#define arrpop stb_arr_pop -#define arrlast stb_arr_last -#define arrlastn stb_arr_lastn -#define arrfor stb_arr_for -#define arraddn stb_arr_addn -#define arrsetsize stb_arr_setsize -#endif - // simple functions written on top of other functions #define stb_arr_empty(a) ( stb_arr_len(a) == 0 ) #define stb_arr_add(a) ( stb_arr_addn((a),1) ) @@ -3296,6 +2636,23 @@ typedef struct // a pointer to the first new one #define stb_arr_addn(a,n) (stb_arr__addn(a,n),(a)+stb_arr_len(a)-(n)) +// add N new uninitialized elements starting at index 'i' +#define stb_arr_insertn(a,i,n) ((a) = stb__arr_insertn(a, sizeof(*a), i, n)) + +// insert an element at i +#define stb_arr_insert(a,i,v) ((a) = stb__arr_insertn(a, sizeof(*a), i, n), ((a)[i] = v)) + +// delete N elements from the middle starting at index 'i' +#define stb_arr_deleten(a,i,n) ((a) = stb__arr_deleten(a, sizeof(*a), i, n)) + +// delete the i'th element +#define stb_arr_delete(a,i) stb_arr_deleten(a,i,1) + +// delete the i'th element, swapping down from the end +#define stb_arr_fastdelete(a,i) \ + (stb_swap(&a[i], &a[stb_arrhead(a)->len-1], sizeof(*a)), stb_arr_pop(a)) + + // ARRAY STORAGE // get the array maximum storage; special case if NULL @@ -3324,7 +2681,8 @@ STB_EXTERN void *stb__arr_copy_(void *p, int elem_size); STB_EXTERN void *stb__arr_setsize_(void *p, int size, int limit STB__PARAMS); STB_EXTERN void *stb__arr_setlen_(void *p, int size, int newlen STB__PARAMS); STB_EXTERN void *stb__arr_addlen_(void *p, int size, int addlen STB__PARAMS); - +STB_EXTERN void *stb__arr_deleten_(void *p, int size, int loc, int n STB__PARAMS); +STB_EXTERN void *stb__arr_insertn_(void *p, int size, int loc, int n STB__PARAMS); #ifdef __cplusplus @@ -3355,6 +2713,10 @@ template struct stb_arr { return stb_arr(stb__arr_setlen_(ptr, size, newlen STB__ARGS)); } stb_arr stb__arr_addlen__(int size, int addlen STB__PARAMS) { return stb_arr(stb__arr_addlen_(ptr, size, addlen STB__ARGS)); } + stb_arr stb__arr_deleten__(int size, int loc, int n STB__PARAMS) + { return stb_arr(stb__arr_deleten_(ptr, size, loc, n STB__ARGS)); } + stb_arr stb__arr_insertn__(int size, int loc, int n STB__PARAMS) + { return stb_arr(stb__arr_insertn_(ptr, size, loc, n STB__ARGS)); } }; #define stb_arr_free(p) (p).stb_arr_free__() @@ -3364,10 +2726,14 @@ template struct stb_arr #define stb__arr_setsize(p,s,n) (p).stb__arr_setsize__(s,n) #define stb__arr_setlen(p,s,n) (p).stb__arr_setlen__(s,n) #define stb__arr_addlen(p,s,n) (p).stb__arr_addlen__(s,n) + #define stb__arr_deleten(p,s,i,n) (p).stb__arr_deleten__(s,i,n) + #define stb__arr_insertn(p,s,i,n) (p).stb__arr_insertn__(s,i,n) #else #define stb__arr_setsize(p,s,n) (p).stb__arr_setsize__(s,n,__FILE__,__LINE__) #define stb__arr_setlen(p,s,n) (p).stb__arr_setlen__(s,n,__FILE__,__LINE__) #define stb__arr_addlen(p,s,n) (p).stb__arr_addlen__(s,n,__FILE__,__LINE__) + #define stb__arr_deleten(p,s,i,n) (p).stb__arr_deleten__(s,i,n,__FILE__,__LINE__) + #define stb__arr_insertn(p,s,i,n) (p).stb__arr_insertn__(s,i,n,__FILE__,__LINE__) #endif #else @@ -3381,10 +2747,14 @@ template struct stb_arr #define stb__arr_setsize stb__arr_setsize_ #define stb__arr_setlen stb__arr_setlen_ #define stb__arr_addlen stb__arr_addlen_ + #define stb__arr_deleten stb__arr_deleten_ + #define stb__arr_insertn stb__arr_insertn_ #else - #define stb__arr_addlen(p,s,n) stb__arr_addlen_(p,s,n,__FILE__,__LINE__) - #define stb__arr_setlen(p,s,n) stb__arr_setlen_(p,s,n,__FILE__,__LINE__) - #define stb__arr_setsize(p,s,n) stb__arr_setsize_(p,s,n,__FILE__,__LINE__) + #define stb__arr_addlen(p,s,n) stb__arr_addlen_(p,s,n,__FILE__,__LINE__) + #define stb__arr_setlen(p,s,n) stb__arr_setlen_(p,s,n,__FILE__,__LINE__) + #define stb__arr_setsize(p,s,n) stb__arr_setsize_(p,s,n,__FILE__,__LINE__) + #define stb__arr_deleten(p,s,i,n) stb__arr_deleten_(p,s,i,n,__FILE__,__LINE__) + #define stb__arr_insertn(p,s,i,n) stb__arr_insertn_(p,s,i,n,__FILE__,__LINE__) #endif #endif @@ -3500,6 +2870,31 @@ void *stb__arr_addlen_(void *p, int size, int addlen STB__PARAMS) { return stb__arr_setlen_(p, size, stb_arr_len2(p) + addlen STB__ARGS); } + +void *stb__arr_insertn_(void *p, int size, int i, int n STB__PARAMS) +{ + if (n) { + int z; + + if (p == NULL) + return stb__arr_addlen_(p, size, n STB__ARGS); + + z = stb_arr_len2(p); + p = stb__arr_addlen_(p, size, i STB__ARGS); + memmove((char *) p + (i+n)*size, (char *) p + i*size, size * (z-i)); + } + return p; +} + +void *stb__arr_deleten_(void *p, int size, int i, int n STB__PARAMS) +{ + if (n) { + memmove((char *) p + i*size, (char *) p + (i+n)*size, size * (stb_arr_len2(p)-i)); + stb_arrhead2(p)->len -= n; + } + return p; +} + #endif ////////////////////////////////////////////////////////////////////////////// @@ -3532,6 +2927,7 @@ STB_EXTERN unsigned int stb_hashlen(char *str, int len); STB_EXTERN unsigned int stb_rehash_improved(unsigned int v); STB_EXTERN unsigned int stb_hash_fast(void *p, int len); STB_EXTERN unsigned int stb_hash2(char *str, unsigned int *hash2_ptr); +STB_EXTERN unsigned int stb_hash_number(unsigned int hash); #define stb_rehash(x) ((x) + ((x) >> 6) + ((x) >> 19)) @@ -3641,16 +3037,28 @@ unsigned int stb_hash_fast(void *p, int len) case 0: break; } - /* Force "avalanching" of final 127 bits */ - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 4; - hash += hash >> 17; - hash ^= hash << 25; - hash += hash >> 6; + /* Force "avalanching" of final 127 bits */ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; - return hash; + return hash; } + +unsigned int stb_hash_number(unsigned int hash) +{ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + #endif ////////////////////////////////////////////////////////////////////////////// @@ -4000,8 +3408,6 @@ int stb_ischar(char c, char *set) #define STB_nullvalue(x) x #define STB_safecompare(x) x #define STB_nosafe(x) -#define STB_hasvalue(x) x -#define STB_novalue(x) #ifdef __GNUC__ #define STB__nogcc(x) @@ -4192,8 +3598,8 @@ int STB__(N, remove)(TYPE *a, KEY k, VALUE *v) \ { \ unsigned int h = STB_(N, hash)(k); \ unsigned int n = h & a->mask, s; \ - if (k == EMPTY) { if (a->has_empty) { *v = a->ev; a->has_empty=0; return 1; } return 0; } \ - if (k == DEL ) { if (a->has_del ) { *v = a->dv; a->has_del =0; return 1; } return 0; } \ + if (k == EMPTY) { if (a->has_empty) { if(v)*v = a->ev; a->has_empty=0; return 1; } return 0; } \ + if (k == DEL ) { if (a->has_del ) { if(v)*v = a->dv; a->has_del =0; return 1; } return 0; } \ if (a->table[n].k == EMPTY) return 0; \ if (SAFE(a->table[n].k == DEL || ) !COMPARE(a->table[n].k,k)) { \ s = stb_rehash(h) | 1; \ @@ -4259,7 +3665,7 @@ static void STB_(N, rehash)(TYPE *a, int count) \ #define stb_define_hash(TYPE,N,KEY,EMPTY,DEL,HASH,VALUE) \ stb_define_hash_base(TYPE,STB_nofields,N,0.85f, \ KEY,EMPTY,DEL,STB_nocopy,STB_nodelete,STB_nosafe,STB_equal,HASH,\ - VALUE,STB_nullvalue,NULL) + VALUE,STB_nonullvalue,0) ////////////////////////////////////////////////////////////////////////////// // @@ -4269,16 +3675,14 @@ static void STB_(N, rehash)(TYPE *a, int count) \ // application is to let you store "extra" data associated with pointers, // which is why it was originally called stb_extra. -//$ SKIP! stb_declare_hash(STB_EXTERN, stb_ptrmap, stb_ptrmap_, void *, void *) -//$ SKIP! stb_declare_hash(STB_EXTERN, stb_idict, stb_idict_, stb_int32, stb_int32) STB_EXTERN void stb_ptrmap_delete(stb_ptrmap *e, void (*free_func)(void *)); STB_EXTERN stb_ptrmap *stb_ptrmap_new(void); STB_EXTERN stb_idict * stb_idict_new_size(int size); -STB_EXTERN void stb_idict_remove_all(stb_idict *e); +STB_EXTERN void stb_idict_remove_all(stb_idict *e); #ifdef STB_DEFINE @@ -4344,9 +3748,8 @@ void stb_idict_remove_all(stb_idict *e) // if "use_arena=1", then strings will be copied // into blocks and never freed until the sdict is freed; // otherwise they're malloc()ed and free()d on the fly. -// (specify use_arena=1 if you never stb_sdict_remove/change) +// (specify use_arena=1 if you never stb_sdict_remove) -//$ SKIP! stb_declare_hash(STB_EXTERN, stb_sdict, stb_sdict_, char *, void *) STB_EXTERN stb_sdict * stb_sdict_new(int use_arena); @@ -4354,8 +3757,8 @@ STB_EXTERN void stb_sdict_delete(stb_sdict *); STB_EXTERN void * stb_sdict_change(stb_sdict *, char *str, void *p); STB_EXTERN int stb_sdict_count(stb_sdict *d); -#define stb_sdict_for(d,i,q,v) \ - for(i=0; i < (d)->limit ? q=(d)->p[i].str,v=(d)->p[i].val,1 : 0; ++i) \ +#define stb_sdict_for(d,i,q,z) \ + for(i=0; i < (d)->limit ? q=(d)->table[i].k,z=(d)->table[i].v,1 : 0; ++i) \ if (q==NULL||q==(void *) 1);else // reversed makes macro friendly #ifdef STB_DEFINE @@ -4363,10 +3766,6 @@ STB_EXTERN int stb_sdict_count(stb_sdict *d); #define STB_DEL ((void *) 1) #define STB_SDEL ((char *) 1) -//#define stb_define_hash_base(TYPE,FIELDS,N,LOAD_FACTOR, - //KEY,EMPTY,DEL,COPY,DISPOSE,COMPARE,HASH, - //VALUE,HASVNULL,VNULL) - #define stb_sdict__copy(x) \ strcpy(a->arena ? stb_malloc_string(a->arena, strlen(x)+1) \ : (char *) malloc(strlen(x)+1), x) @@ -4682,25 +4081,6 @@ TYPE *STB__(M,Find)(TREE *tree, VTYPE a) \ stb_bst_raw(TYPE,N,TREE,M,vfield,VTYPE,compare,stb__bst_parent) -////////////////////////////////////////////////////////////////////////////// -// -// Stream Processing -// -// Stream processing allows you to: -// redirect file I/O to/from a memory buffer -// turn on and off stream/compression -// while compressing, you cannot ftell/fseek -// -// -// implements: -// fopen, fclose -// fwrite, fread -// fputc, fgetc -// - - - - ////////////////////////////////////////////////////////////////////////////// // @@ -4708,9 +4088,8 @@ TYPE *STB__(M,Find)(TREE *tree, VTYPE a) \ // -#ifdef _WIN32 +#ifdef _MSC_VER // if not win32, we can still get gcc style int64! - // and this should probably be _MSC_VER ? typedef unsigned _int64 stb__64; #define stb_rename(x,y) _wrename(stb__from_utf8(x), stb__from_utf8_alt(y)) @@ -4728,19 +4107,18 @@ TYPE *STB__(M,Find)(TREE *tree, VTYPE a) \ #define stb_fileu (unsigned char *) stb_file STB_EXTERN void * stb_file(char *filename, size_t *length); STB_EXTERN size_t stb_filelen(FILE *f); +STB_EXTERN int stb_filewrite(char *filename, void *data, size_t length); +STB_EXTERN int stb_filewritestr(char *filename, char *data); STB_EXTERN char ** stb_stringfile(char *filename, int *len); STB_EXTERN char ** stb_stringfile_trimmed(char *name, int *len, char comm); STB_EXTERN char * stb_fgets(char *buffer, int buflen, FILE *f); STB_EXTERN char * stb_fgets_malloc(FILE *f); STB_EXTERN int stb_fexists(char *filename); -STB_EXTERN void stb_fwrite32(FILE *f, uint32 datum); STB_EXTERN int stb_fullpath(char *abs, int abs_size, char *rel); STB_EXTERN FILE * stb_fopen(char *filename, char *mode); STB_EXTERN int stb_fclose(FILE *f, int keep); -STB_EXTERN int stb_copyfile(char *src, char *dest); - enum { stb_keep_no = 0, @@ -4748,6 +4126,28 @@ enum stb_keep_if_different = 2, }; +STB_EXTERN int stb_copyfile(char *src, char *dest); + +#ifdef _MSC_VER + // if not win32, we can still get gcc style int64! + typedef unsigned __int64 stb__64; + STB_EXTERN void stb_fput_varlen64(FILE *f, stb__64 v); + STB_EXTERN stb__64 stb_fget_varlen64(FILE *f); + STB_EXTERN int stb_size_varlen64(stb__64 v); +#endif + +STB_EXTERN void stb_fwrite32(FILE *f, stb_uint32 datum); +STB_EXTERN void stb_fput_varlen (FILE *f, int v); +STB_EXTERN void stb_fput_varlenu(FILE *f, unsigned int v); +STB_EXTERN int stb_fget_varlen (FILE *f); +STB_EXTERN stb_uint stb_fget_varlenu(FILE *f); +STB_EXTERN void stb_fput_ranged (FILE *f, int v, int b, stb_uint n); +STB_EXTERN int stb_fget_ranged (FILE *f, int b, stb_uint n); +STB_EXTERN int stb_size_varlen (int v); +STB_EXTERN int stb_size_varlenu(unsigned int v); +STB_EXTERN int stb_size_ranged (int b, stb_uint n); + + #ifdef STB_DEFINE @@ -4756,7 +4156,7 @@ void stb_fwrite32(FILE *f, uint32 x) fwrite(&x, 4, 1, f); } -#ifdef _WIN32 +#ifdef _MSC_VER #define stb__stat _stat #else #define stb__stat stat @@ -4800,6 +4200,21 @@ void *stb_file(char *filename, size_t *length) return buffer; } +int stb_filewrite(char *filename, void *data, size_t length) +{ + FILE *f = stb_fopen(filename, "wb"); + if (f) { + fwrite(data, 1, length, f); + stb_fclose(f, stb_keep_if_different); + } + return f != NULL; +} + +int stb_filewritestr(char *filename, char *data) +{ + return stb_filewrite(filename, data, strlen(data)); +} + void * stb_file_max(char *filename, size_t *length) { FILE *f = stb__fopen(filename, "rb"); @@ -4929,7 +4344,7 @@ char * stb_fgets_malloc(FILE *f) int stb_fullpath(char *abs, int abs_size, char *rel) { - #ifdef _WIN32 + #ifdef _MSC_VER return _fullpath(abs, rel, abs_size) != NULL; #else if (abs[0] == '/' || abs[0] == '~') { @@ -5162,8 +4577,213 @@ int stb_copyfile(char *src, char *dest) return TRUE; } +// varlen: +// v' = (v >> 31) + (v < 0 ? ~v : v)<<1; // small abs(v) => small v' +// output v as big endian v'+k for v' <= k: +// 1 byte : v' <= 0x00000080 ( -64 <= v < 64) 7 bits +// 2 bytes: v' <= 0x00004000 (-8192 <= v < 8192) 14 bits +// 3 bytes: v' <= 0x00200000 21 bits +// 4 bytes: v' <= 0x10000000 28 bits +// the number of most significant 1-bits in the first byte +// equals the number of bytes after the first + +#define stb__varlen_xform(v) (v<0 ? (~v << 1)+1 : (v << 1)) + +int stb_size_varlen(int v) { return stb_size_varlenu(stb__varlen_xform(v)); } +int stb_size_varlenu(unsigned int v) +{ + if (v < 0x00000080) return 1; + if (v < 0x00004000) return 2; + if (v < 0x00200000) return 3; + if (v < 0x10000000) return 4; + return 5; +} + +void stb_fput_varlen(FILE *f, int v) { stb_fput_varlenu(f, stb__varlen_xform(v)); } + +void stb_fput_varlenu(FILE *f, unsigned int z) +{ + if (z >= 0x10000000) fputc(0xF0,f); + if (z >= 0x00200000) fputc((z < 0x10000000 ? 0xE0 : 0)+(z>>24),f); + if (z >= 0x00004000) fputc((z < 0x00200000 ? 0xC0 : 0)+(z>>16),f); + if (z >= 0x00000080) fputc((z < 0x00004000 ? 0x80 : 0)+(z>> 8),f); + fputc(z,f); +} + +#define stb_fgetc(f) ((unsigned char) fgetc(f)) + +int stb_fget_varlen(FILE *f) +{ + unsigned int z = stb_fget_varlenu(f); + return (z & 1) ? ~(z>>1) : (z>>1); +} + +unsigned int stb_fget_varlenu(FILE *f) +{ + unsigned int z; + unsigned char d; + d = stb_fgetc(f); + + if (d >= 0x80) { + if (d >= 0xc0) { + if (d >= 0xe0) { + if (d == 0xf0) z = stb_fgetc(f) << 24; + else z = (d - 0xe0) << 24; + z += stb_fgetc(f) << 16; + } + else + z = (d - 0xc0) << 16; + z += stb_fgetc(f) << 8; + } else + z = (d - 0x80) << 8; + z += stb_fgetc(f); + } else + z = d; + return z; +} + +#ifdef _MSC_VER + +stb__64 stb_fget_varlen64(FILE *f) +{ + stb__64 z; + unsigned char d; + d = stb_fgetc(f); + + if (d >= 0x80) { + if (d >= 0xc0) { + if (d >= 0xe0) { + if (d >= 0xf0) { + if (d >= 0xf8) { + if (d >= 0xfc) { + if (d >= 0xfe) { + if (d >= 0xff) + z = (stb__64) stb_fgetc(f) << 56; + else + z = (stb__64) (d - 0xfe) << 56; + z |= (stb__64) stb_fgetc(f) << 48; + } else z = (stb__64) (d - 0xfc) << 48; + z |= (stb__64) stb_fgetc(f) << 40; + } else z = (stb__64) (d - 0xf8) << 40; + z |= (stb__64) stb_fgetc(f) << 32; + } else z = (stb__64) (d - 0xf0) << 32; + z |= (stb_uint) stb_fgetc(f) << 24; + } else z = (stb_uint) (d - 0xe0) << 24; + z |= (stb_uint) stb_fgetc(f) << 16; + } else z = (stb_uint) (d - 0xc0) << 16; + z |= (stb_uint) stb_fgetc(f) << 8; + } else z = (stb_uint) (d - 0x80) << 8; + z |= stb_fgetc(f); + } else + z = d; + + return (z & 1) ? ~(z >> 1) : (z >> 1); +} + +int stb_size_varlen64(stb__64 v) +{ + if (v < 0x00000080) return 1; + if (v < 0x00004000) return 2; + if (v < 0x00200000) return 3; + if (v < 0x10000000) return 4; + if (v < 0x0000000800000000i64) return 5; + if (v < 0x0000040000000000i64) return 6; + if (v < 0x0002000000000000i64) return 7; + if (v < 0x0100000000000000i64) return 8; + return 9; +} + +void stb_fput_varlen64(FILE *f, stb__64 v) +{ + stb__64 z = stb__varlen_xform(v); + int first=1; + if (z >= 0x100000000000000i64) { + fputc(0xff,f); + first=0; + } + if (z >= 0x02000000000000i64) fputc((first ? 0xFE : 0)+(int)(z>>56),f), first=0; + if (z >= 0x00040000000000i64) fputc((first ? 0xFC : 0)+(int)(z>>48),f), first=0; + if (z >= 0x00000800000000i64) fputc((first ? 0xF8 : 0)+(int)(z>>40),f), first=0; + if (z >= 0x00000010000000i64) fputc((first ? 0xF0 : 0)+(int)(z>>32),f), first=0; + if (z >= 0x00000000200000i64) fputc((first ? 0xE0 : 0)+(int)(z>>24),f), first=0; + if (z >= 0x00000000004000i64) fputc((first ? 0xC0 : 0)+(int)(z>>16),f), first=0; + if (z >= 0x00000000000080i64) fputc((first ? 0x80 : 0)+(int)(z>> 8),f), first=0; + fputc((int)z,f); +} #endif +void stb_fput_ranged(FILE *f, int v, int b, stb_uint n) +{ + v -= b; + if (n <= (1 << 31)) + assert((stb_uint) v < n); + if (n > (1 << 24)) fputc(v >> 24, f); + if (n > (1 << 16)) fputc(v >> 16, f); + if (n > (1 << 8)) fputc(v >> 8, f); + fputc(v,f); +} + +int stb_fget_ranged(FILE *f, int b, stb_uint n) +{ + unsigned int v=0; + if (n > (1 << 24)) v += stb_fgetc(f) << 24; + if (n > (1 << 16)) v += stb_fgetc(f) << 16; + if (n > (1 << 8)) v += stb_fgetc(f) << 8; + v += stb_fgetc(f); + return b+v; +} + +int stb_size_ranged(int b, stb_uint n) +{ + if (n > (1 << 24)) return 4; + if (n > (1 << 16)) return 3; + if (n > (1 << 8)) return 2; + return 1; +} + +void stb_fput_string(FILE *f, char *s) +{ + int len = strlen(s); + stb_fput_varlenu(f, len); + fwrite(s, 1, len, f); +} + +// inverse of the above algorithm +char *stb_fget_string(FILE *f, void *p) +{ + char *s; + int len = stb_fget_varlenu(f); + if (len > 4096) return NULL; + s = p ? stb_malloc_string(p, len+1) : (char *) malloc(len+1); + fread(s, 1, len, f); + s[len] = 0; + return s; +} + +char *stb_strdup(char *str, void *pool) +{ + int len = strlen(str); + char *p = stb_malloc_string(pool, len+1); + strcpy(p, str); + return p; +} + +// strip the trailing '/' or '\\' from a directory so we can refer to it +// as a file for _stat() +char *stb_strip_final_slash(char *t) +{ + if (t[0]) { + char *z = t + strlen(t) - 1; + // *z is the last character + if (*z == '\\' || *z == '//') + if (z != t+2 || t[1] != ':') // but don't strip it if it's e.g. "c:/" + *z = 0; + if (*z == '\\') + *z = '/'; // canonicalize to make sure it matches db + } + return t; +} +#endif ////////////////////////////////////////////////////////////////////////////// // @@ -5254,12 +4874,11 @@ STB_EXTERN char **stb_readdir_files_mask(char *dir, char *wild); STB_EXTERN char **stb_readdir_subdirs(char *dir); STB_EXTERN void stb_readdir_free (char **files); STB_EXTERN char **stb_readdir_recursive(char *dir, char *filespec); -STB_EXTERN char **stb_readdir_recursive_n(char *dir, char **filespecs, int n); STB_EXTERN void stb_delete_directory_recursive(char *dir); #ifdef STB_DEFINE -#ifdef _WIN32 +#ifdef _MSC_VER #include #else #include @@ -5283,7 +4902,7 @@ static char **readdir_raw(char *dir, int return_subdirs, char *mask) char buffer[512], with_slash[512]; int n; - #ifdef _WIN32 + #ifdef _MSC_VER stb__wchar *ws; struct _wfinddata_t data; const long none = -1; @@ -5303,7 +4922,7 @@ static char **readdir_raw(char *dir, int return_subdirs, char *mask) buffer[n] = 0; strcpy(with_slash, buffer); - #ifdef _WIN32 + #ifdef _MSC_VER strcpy(buffer+n, "*.*"); ws = stb__from_utf8(buffer); z = _wfindfirst(ws, &data); @@ -5314,7 +4933,7 @@ static char **readdir_raw(char *dir, int return_subdirs, char *mask) if (z != none) { int nonempty = TRUE; - #ifndef _WIN32 + #ifndef _MSC_VER struct dirent *data = readdir(z); nonempty = (data != NULL); #endif @@ -5323,7 +4942,7 @@ static char **readdir_raw(char *dir, int return_subdirs, char *mask) do { int is_subdir; - #ifdef _WIN32 + #ifdef _MSC_VER char *name = stb__to_utf8(data.name); if (name == NULL) { printf("Unable to convert '%S' to utf8!\n", data.name); @@ -5346,7 +4965,7 @@ static char **readdir_raw(char *dir, int return_subdirs, char *mask) if (buffer[0] == '.' && buffer[1] == '/') p = buffer+2; stb_arr_push(results, strdup(p)); - #ifdef _WIN32 + #ifdef _MSC_VER if (!is_subdir) stb_readdir_size += data.size; #endif @@ -5354,13 +4973,13 @@ static char **readdir_raw(char *dir, int return_subdirs, char *mask) } } } - #ifdef _WIN32 + #ifdef _MSC_VER while (0 == _wfindnext(z, &data)); #else while ((data = readdir(z)) != NULL); #endif } - #ifdef _WIN32 + #ifdef _MSC_VER _findclose(z); #else closedir(z); @@ -5373,39 +4992,34 @@ char **stb_readdir_files (char *dir) { return readdir_raw(dir, 0, NULL); } char **stb_readdir_subdirs(char *dir) { return readdir_raw(dir, 1, NULL); } char **stb_readdir_files_mask(char *dir, char *wild) { return readdir_raw(dir, 0, wild); } -static char **stb_readdir_rec(STB__ARR(char *) sofar, char *dir, char **filespecs, int num_specs) +int stb__rec_max=0x7fffffff; +static char **stb_readdir_rec(STB__ARR(char *) sofar, char *dir, char *filespec) { - int i, n = strcmp(dir, ".") ? strlen(dir)+1 : 0; + int n = strcmp(dir, ".") ? strlen(dir)+1 : 0; STB__ARR(char *) files; STB__ARR(char *) dirs; char **p; - files = stb_readdir_files(dir); - if (filespecs == NULL) num_specs = 0; + + if (stb_arr_len(sofar) >= stb__rec_max) return sofar; + + files = stb_readdir_files_mask(dir, filespec); stb_arr_for(p, files) { - for (i=0; i < num_specs; ++i) - if (stb_wildmatchi(filespecs[i], *p + n)) - break; - if (num_specs == 0 || i < num_specs) - stb_arr_push(sofar, strdup(*p)); + stb_arr_push(sofar, strdup(*p)); + if (stb_arr_len(sofar) >= stb__rec_max) break; } stb_readdir_free(files); + if (stb_arr_len(sofar) >= stb__rec_max) return sofar; dirs = stb_readdir_subdirs(dir); stb_arr_for(p, dirs) - sofar = stb_readdir_rec(sofar, *p, filespecs, num_specs); + sofar = stb_readdir_rec(sofar, *p, filespec); stb_readdir_free(dirs); return sofar; } -char **stb_readdir_recursive_n(char *dir, char **filespecs, int num_specs) -{ - return stb_readdir_rec(NULL, dir, filespecs, num_specs); -} - char **stb_readdir_recursive(char *dir, char *filespec) { - char *filespecs[2] = { filespec, NULL }; - return stb_readdir_recursive_n(dir, filespecs, filespec ? 1 : 0); + return stb_readdir_rec(NULL, dir, filespec); } void stb_delete_directory_recursive(char *dir) @@ -5420,7 +5034,7 @@ void stb_delete_directory_recursive(char *dir) if (!remove(list[i])) { // on windows, try again after making it writeable; don't ALWAYS // do this first since that would be slow in the normal case - #ifdef _WIN32 + #ifdef _MSC_VER _chmod(list[i], _S_IWRITE); remove(list[i]); #endif @@ -5435,11 +5049,11 @@ void stb_delete_directory_recursive(char *dir) // // construct trees from filenames; useful for cmirror summaries -typedef struct stb_dirtree stb_dirtree; +typedef struct stb_dirtree2 stb_dirtree2; -struct stb_dirtree +struct stb_dirtree2 { - STB__ARR(stb_dirtree *) subdirs; + STB__ARR(stb_dirtree2 *) subdirs; // make convenient for stb_summarize_tree int num_subdir; @@ -5451,8 +5065,8 @@ struct stb_dirtree STB__ARR(char *) files; }; -STB_EXTERN stb_dirtree *stb_dirtree_from_files_relative(char *src, char **filelist, int count); -STB_EXTERN stb_dirtree *stb_dirtree_from_files(char **filelist, int count); +STB_EXTERN stb_dirtree2 *stb_dirtree2_from_files_relative(char *src, char **filelist, int count); +STB_EXTERN stb_dirtree2 *stb_dirtree2_from_files(char **filelist, int count); STB_EXTERN int stb_dir_is_prefix(char *dir, int dirlen, char *file); #ifdef STB_DEFINE @@ -5465,12 +5079,12 @@ int stb_dir_is_prefix(char *dir, int dirlen, char *file) return FALSE; } -stb_dirtree *stb_dirtree_from_files_relative(char *src, char **filelist, int count) +stb_dirtree2 *stb_dirtree2_from_files_relative(char *src, char **filelist, int count) { char buffer1[1024]; int i; int dlen = strlen(src), elen; - stb_dirtree *d; + stb_dirtree2 *d; STB__ARR(char *) descendents = NULL; STB__ARR(char *) files = NULL; char *s; @@ -5499,7 +5113,7 @@ stb_dirtree *stb_dirtree_from_files_relative(char *src, char **filelist, int cou ++i; } // now create a record - d = (stb_dirtree *) malloc(sizeof(*d)); + d = (stb_dirtree2 *) malloc(sizeof(*d)); d->files = files; d->subdirs = NULL; d->fullpath = strdup(src); @@ -5519,8 +5133,8 @@ stb_dirtree *stb_dirtree_from_files_relative(char *src, char **filelist, int cou t = stb_strchr2(s, '/', '\\'); assert(t); stb_strncpy(buffer2, descendents[i], t-descendents[i]+1); - if (stricmp(buffer1, buffer2)) { - stb_dirtree *t = stb_dirtree_from_files_relative(buffer2, descendents, stb_arr_len(descendents)); + if (stb_stricmp(buffer1, buffer2)) { + stb_dirtree2 *t = stb_dirtree2_from_files_relative(buffer2, descendents, stb_arr_len(descendents)); assert(t != NULL); strcpy(buffer1, buffer2); stb_arr_push(d->subdirs, t); @@ -5531,9 +5145,9 @@ stb_dirtree *stb_dirtree_from_files_relative(char *src, char **filelist, int cou return d; } -stb_dirtree *stb_dirtree_from_files(char **filelist, int count) +stb_dirtree2 *stb_dirtree2_from_files(char **filelist, int count) { - return stb_dirtree_from_files_relative("", filelist, count); + return stb_dirtree2_from_files_relative("", filelist, count); } #endif @@ -5725,7 +5339,7 @@ void stb_sha1(stb_uchar output[20], stb_uchar *buffer, stb_uint len) } } -#ifdef _WIN32 +#ifdef _MSC_VER // @TODO: rewrite this to not use 64-bit numbers, e.g. // manually use 2 32-bit ints (or get stb__64 ported) int stb_sha1_file(stb_uchar output[20], char *file) @@ -5793,7 +5407,7 @@ int stb_sha1_file(stb_uchar output[20], char *file) return 1; } -#endif +#endif // _MSC_VER // client can truncate this wherever they like void stb_sha1_readable(char display[27], unsigned char sha[20]) @@ -5819,8 +5433,653 @@ void stb_sha1_readable(char display[27], unsigned char sha[20]) display[o++] = encoding[acc]; } +#endif // STB_DEFINE + +/////////////////////////////////////////////////////////// +// +// simplified WINDOWS registry interface... hopefully +// we'll never actually use this? + +#ifdef _WIN32 +STB_EXTERN void * stb_reg_open(char *mode, char *where); // mode: "rHKLM" or "rHKCU" or "w.." +STB_EXTERN void stb_reg_close(void *reg); +STB_EXTERN int stb_reg_read(void *zreg, char *str, void *data, int len); +STB_EXTERN int stb_reg_read_string(void *zreg, char *str, char *data, int len); +STB_EXTERN void stb_reg_write(void *zreg, char *str, void *data, int len); +STB_EXTERN void stb_reg_write_string(void *zreg, char *str, char *data); + +#ifdef STB_DEFINE +#ifndef _WINDOWS_ + +#define HKEY void * + +STB_EXTERN __declspec(dllimport) long __stdcall RegCloseKey ( HKEY hKey ); +STB_EXTERN __declspec(dllimport) long __stdcall RegCreateKeyExA ( HKEY hKey, const char * lpSubKey, + int Reserved, char * lpClass, int dwOptions, + int samDesired, void *lpSecurityAttributes, HKEY * phkResult, int * lpdwDisposition ); +STB_EXTERN __declspec(dllimport) long __stdcall RegDeleteKeyA ( HKEY hKey, const char * lpSubKey ); +STB_EXTERN __declspec(dllimport) long __stdcall RegQueryValueExA ( HKEY hKey, const char * lpValueName, + int * lpReserved, int * lpType, unsigned char * lpData, int * lpcbData ); +STB_EXTERN __declspec(dllimport) long __stdcall RegSetValueExA ( HKEY hKey, const char * lpValueName, + int Reserved, int dwType, const unsigned char* lpData, int cbData ); +STB_EXTERN __declspec(dllimport) long __stdcall RegOpenKeyExA ( HKEY hKey, const char * lpSubKey, + int ulOptions, int samDesired, HKEY * phkResult ); + +#endif // _WINDOWS_ + +#define STB__REG_OPTION_NON_VOLATILE 0 +#define STB__REG_KEY_ALL_ACCESS 0x000f003f +#define STB__REG_KEY_READ 0x00020019 + +void *stb_reg_open(char *mode, char *where) +{ + long res; + HKEY base; + HKEY zreg; + if (!stricmp(mode+1, "cu") || !stricmp(mode+1, "hkcu")) + base = (HKEY) 0x80000001; // HKCU + else if (!stricmp(mode+1, "lm") || !stricmp(mode+1, "hklm")) + base = (HKEY) 0x80000002; // HKLM + else + return NULL; + + if (mode[0] == 'r') + res = RegOpenKeyExA(base, where, 0, STB__REG_KEY_READ, &zreg); + else if (mode[0] == 'w') + res = RegCreateKeyExA(base, where, 0, NULL, STB__REG_OPTION_NON_VOLATILE, STB__REG_KEY_ALL_ACCESS, NULL, &zreg, NULL); + else + return NULL; + + return res ? NULL : zreg; +} + +void stb_reg_close(void *reg) +{ + RegCloseKey((HKEY) reg); +} + +#define STB__REG_SZ 1 +#define STB__REG_BINARY 3 +#define STB__REG_DWORD 4 + +int stb_reg_read(void *zreg, char *str, void *data, int len) +{ + int type; + int alen = len; + if (0 == RegQueryValueExA((HKEY) zreg, str, 0, &type, (unsigned char *) data, &len)) + if (type == STB__REG_BINARY || type == STB__REG_SZ || type == STB__REG_DWORD) { + if (len < alen) + *((char *) data + len) = 0; + return 1; + } + return 0; +} + +void stb_reg_write(void *zreg, char *str, void *data, int len) +{ + if (zreg) + RegSetValueExA((HKEY) zreg, str, 0, STB__REG_BINARY, (const unsigned char *) data, len); +} + +int stb_reg_read_string(void *zreg, char *str, char *data, int len) +{ + if (!stb_reg_read(zreg, str, data, len)) return 0; + data[len-1] = 0; // force a 0 at the end of the string no matter what + return 1; +} + +void stb_reg_write_string(void *zreg, char *str, char *data) +{ + if (zreg) + RegSetValueExA((HKEY) zreg, str, 0, STB__REG_SZ, (const unsigned char *) data, strlen(data)+1); +} +#endif // STB_DEFINE +#endif // _WIN32 + + +////////////////////////////////////////////////////////////////////////////// +// +// stb_cfg - This is like the registry, but the config info +// is all stored in plain old files where we can +// backup and restore them easily. The LOCATION of +// the config files is gotten from... the registry! + +#ifndef STB_NO_STB_STRINGS +typedef struct stb_cfg_st stb_cfg; + +STB_EXTERN stb_cfg * stb_cfg_open(char *config, char *mode); // mode = "r", "w" +STB_EXTERN void stb_cfg_close(stb_cfg *cfg); +STB_EXTERN int stb_cfg_read(stb_cfg *cfg, char *key, void *value, int len); +STB_EXTERN void stb_cfg_write(stb_cfg *cfg, char *key, void *value, int len); +STB_EXTERN int stb_cfg_read_string(stb_cfg *cfg, char *key, char *value, int len); +STB_EXTERN void stb_cfg_write_string(stb_cfg *cfg, char *key, char *value); +STB_EXTERN int stb_cfg_delete(stb_cfg *cfg, char *key); + +#ifdef STB_DEFINE + +typedef struct +{ + char *key; + void *value; + int value_len; +} stb__cfg_item; + +struct stb_cfg_st +{ + STB__ARR(stb__cfg_item) data; + char *loaded_file; // this needs to be freed + FILE *f; // write the data to this file on close +}; + +static char *stb__cfg_sig = "sTbCoNfIg!\0\0"; + +STB_EXTERN stb_cfg * stb_cfg_open(char *config, char *mode) +{ + unsigned int len; + stb_cfg *z; + char file[512]; + void *reg; + if (mode[0] != 'r' && mode[0] != 'w') return NULL; + + reg = stb_reg_open("rHKLM", "Software\\SilverSpaceship\\stb"); + strcpy(file, "c:/stb"); + if (reg) { + stb_reg_read_string(reg, "config_dir", file, sizeof(file)); + stb_reg_close(reg); + } + + strcat(file, "/"); + strcat(file, config); + strcat(file, ".cfg"); + + z = (stb_cfg *) stb_malloc(0, sizeof(*z)); + z->data = NULL; + + z->loaded_file = stb_filec(file, &len); + if (z->loaded_file) { + char *s = z->loaded_file; + if (!memcmp(s, stb__cfg_sig, 12)) { + char *s = z->loaded_file + 12; + while (s < z->loaded_file + len) { + stb__cfg_item a; + int n = *(int16 *) s; + a.key = s+2; + s = s+2 + n; + a.value_len = *(int *) s; + s += 4; + a.value = s; + s += a.value_len; + stb_arr_push(z->data, a); + } + assert(s == z->loaded_file + len); + } + } + + if (mode[0] == 'w') + z->f = fopen(file, "wb"); + else + z->f = NULL; + + return z; +} + +void stb_cfg_close(stb_cfg *z) +{ + if (z->f) { + int i; + // write the file out + fwrite(stb__cfg_sig, 12, 1, z->f); + for (i=0; i < stb_arr_len(z->data); ++i) { + int16 n = strlen(z->data[i].key)+1; + fwrite(&n, 2, 1, z->f); + fwrite(z->data[i].key, n, 1, z->f); + fwrite(&z->data[i].value_len, 4, 1, z->f); + fwrite(z->data[i].value, z->data[i].value_len, 1, z->f); + } + fclose(z->f); + } + stb_arr_free(z->data); + stb_free(z); +} + +int stb_cfg_read(stb_cfg *z, char *key, void *value, int len) +{ + int i; + for (i=0; i < stb_arr_len(z->data); ++i) { + if (!stricmp(z->data[i].key, key)) { + int n = stb_min(len, z->data[i].value_len); + memcpy(value, z->data[i].value, n); + if (n < len) + *((char *) value + n) = 0; + return 1; + } + } + return 0; +} + +void stb_cfg_write(stb_cfg *z, char *key, void *value, int len) +{ + int i; + for (i=0; i < stb_arr_len(z->data); ++i) + if (!stricmp(z->data[i].key, key)) + break; + if (i == stb_arr_len(z->data)) { + stb__cfg_item p; + p.key = stb_strdup(key, z); + p.value = NULL; + p.value_len = 0; + stb_arr_push(z->data, p); + } + z->data[i].value = stb_malloc(z, len); + z->data[i].value_len = len; + memcpy(z->data[i].value, value, len); +} + +int stb_cfg_delete(stb_cfg *z, char *key) +{ + int i; + for (i=0; i < stb_arr_len(z->data); ++i) + if (!stricmp(z->data[i].key, key)) { + stb_arr_fastdelete(z->data, i); + return 1; + } + return 0; +} + +int stb_cfg_read_string(stb_cfg *z, char *key, char *value, int len) +{ + if (!stb_cfg_read(z, key, value, len)) return 0; + value[len-1] = 0; + return 1; +} + +void stb_cfg_write_string(stb_cfg *z, char *key, char *value) +{ + stb_cfg_write(z, key, value, strlen(value)+1); +} #endif +////////////////////////////////////////////////////////////////////////////// +// +// stb_dirtree - load a description of a directory tree +// uses a cache and stat()s the directories for changes +// MUCH faster on NTFS, _wrong_ on FAT32, so should +// ignore the db on FAT32 + +#ifdef _WIN32 + +typedef struct +{ + char * path; // full path from passed-in root + time_t last_modified; + int num_files; +} stb_dirtree_dir; + +typedef struct +{ + char *name; // name relative to path + int dir; // index into dirs[] array + unsigned long size; // size, max 4GB + time_t last_modified; +} stb_dirtree_file; + +typedef struct +{ + STB__ARR(stb_dirtree_dir) dirs; + STB__ARR(stb_dirtree_file) files; + + // internal use + void * string_pool; // used to free data en masse +} stb_dirtree; + +extern void stb_dirtree_free ( stb_dirtree *d ); +extern stb_dirtree *stb_dirtree_get ( char *dir); +extern stb_dirtree *stb_dirtree_get_dir ( char *dir, char *cache_dir); +extern stb_dirtree *stb_dirtree_get_with_file ( char *dir, char *cache_file); + +// get a list of all the files recursively underneath 'dir' +// +// cache_file is used to store a copy of the directory tree to speed up +// later calls. It must be unique to 'dir' and the current working +// directory! Otherwise who knows what will happen (a good solution +// is to put it _in_ dir, but this API doesn't force that). +// +// Also, it might be possible to break this if you have two different processes +// do a call to stb_dirtree_get() with the same cache file at about the same +// time, but I _think_ it might just work. + + +#ifdef STB_DEFINE +static void stb__dirtree_add_dir(char *path, time_t last, stb_dirtree *active) +{ + stb_dirtree_dir d; + d.last_modified = last; + d.num_files = 0; + d.path = stb_strdup(path, active->string_pool); + stb_arr_push(active->dirs, d); +} + +static void stb__dirtree_add_file(char *name, int dir, unsigned long size, time_t last, stb_dirtree *active) +{ + stb_dirtree_file f; + f.dir = dir; + f.size = size; + f.last_modified = last; + f.name = stb_strdup(name, active->string_pool); + ++active->dirs[dir].num_files; + stb_arr_push(active->files, f); +} + +static char stb__signature[12] = { 's', 'T', 'b', 'D', 'i', 'R', 't', 'R', 'e', 'E', '0', '1' }; + +static void stb__dirtree_save_db(char *filename, stb_dirtree *data, char *root) +{ + int i, num_dirs_final=0, num_files_final; + int *remap; + FILE *f = fopen(filename, "wb"); + if (!f) return; + + fwrite(stb__signature, sizeof(stb__signature), 1, f); + fwrite(root, strlen(root)+1, 1, f); + // need to be slightly tricky and not write out NULLed directories, nor the root + + // build remapping table of all dirs we'll be writing out + remap = (int *) malloc(sizeof(remap[0]) * stb_arr_len(data->dirs)); + for (i=0; i < stb_arr_len(data->dirs); ++i) { + if (data->dirs[i].path == NULL || 0==stricmp(data->dirs[i].path, root)) { + remap[i] = -1; + } else { + remap[i] = num_dirs_final++; + } + } + + fwrite(&num_dirs_final, 4, 1, f); + for (i=0; i < stb_arr_len(data->dirs); ++i) { + if (remap[i] >= 0) { + fwrite(&data->dirs[i].last_modified, 4, 1, f); + stb_fput_string(f, data->dirs[i].path); + } + } + + num_files_final = 0; + for (i=0; i < stb_arr_len(data->files); ++i) + if (remap[data->files[i].dir] >= 0) + ++num_files_final; + + fwrite(&num_files_final, 4, 1, f); + for (i=0; i < stb_arr_len(data->files); ++i) { + if (remap[data->files[i].dir] >= 0) { + stb_fput_ranged(f, remap[data->files[i].dir], 0, num_dirs_final); + stb_fput_varlenu(f, data->files[i].size); + fwrite(&data->files[i].last_modified, 4, 1, f); + stb_fput_string(f, data->files[i].name); + } + } + + fclose(f); +} + +// note: stomps any existing data, rather than appending +static void stb__dirtree_load_db(char *filename, stb_dirtree *data, char *dir) +{ + char sig[2048]; + int i,n; + FILE *f = fopen(filename, "rb"); + + if (!f) return; + + data->string_pool = stb_malloc(0,1); + + fread(sig, sizeof(stb__signature), 1, f); + if (memcmp(stb__signature, sig, sizeof(stb__signature))) { fclose(f); return; } + if (!fread(sig, strlen(dir)+1, 1, f)) { fclose(f); return; } + if (stricmp(sig,dir)) { fclose(f); return; } + + // we can just read them straight in, because they're guaranteed to be valid + fread(&n, 4, 1, f); + stb_arr_setlen(data->dirs, n); + for(i=0; i < stb_arr_len(data->dirs); ++i) { + fread(&data->dirs[i].last_modified, 4, 1, f); + data->dirs[i].path = stb_fget_string(f, data->string_pool); + if (data->dirs[i].path == NULL) goto bail; + } + fread(&n, 4, 1, f); + stb_arr_setlen(data->files, n); + for (i=0; i < stb_arr_len(data->files); ++i) { + data->files[i].dir = stb_fget_ranged(f, 0, stb_arr_len(data->dirs)); + data->files[i].size = stb_fget_varlenu(f); + fread(&data->files[i].last_modified, 4, 1, f); + data->files[i].name = stb_fget_string(f, data->string_pool); + if (data->files[i].name == NULL) goto bail; + } + + if (0) { + bail: + data->dirs = stb_arr_free(data->dirs); + data->files = stb_arr_free(data->files); + } + fclose(f); +} + +static void stb__dirtree_scandir(char *path, time_t last_time, stb_dirtree *active) +{ + // this is dumb depth first; theoretically it might be faster + // to fully traverse each directory before visiting its children, + // but it's complicated and didn't seem like a gain in the test app + + int n; + + struct _wfinddata_t c_file; + long hFile; + stb__wchar full_path[1024]; + if (path[0] && path[strlen(path)-1] == '/') + swprintf(full_path, L"%s*", stb__from_utf8(path)); + else + swprintf(full_path, L"%s/*", stb__from_utf8(path)); + + // it's possible this directory is already present: that means it was in the + // cache, but its parent wasn't... in that case, we're done with it + for (n=0; n < stb_arr_len(active->dirs); ++n) + if (0 == stricmp(active->dirs[n].path, path)) + return; + + // otherwise, we need to add it + stb__dirtree_add_dir(path, last_time, active); + n = stb_arr_lastn(active->dirs); + + if( (hFile = _wfindfirst( full_path, &c_file )) != -1L ) { + do { + if (c_file.attrib & _A_SUBDIR) { + // ignore subdirectories starting with '.', e.g. "." and ".." + if (c_file.name[0] != '.') { + char *new_path = (char *) full_path; + char *temp = stb__to_utf8(c_file.name); + sprintf(new_path, "%s/%s", path, temp); + stb__dirtree_scandir(new_path, c_file.time_write, active); + } + } else { + char *temp = stb__to_utf8(c_file.name); + stb__dirtree_add_file(temp, n, c_file.size, c_file.time_write, active); + } + } while( _wfindnext( hFile, &c_file ) == 0 ); + + _findclose( hFile ); + } +} + +// scan the database and see if it's all valid +static int stb__dirtree_update_db(stb_dirtree *db, stb_dirtree *active) +{ + int changes_detected = FALSE; + int i; + int *remap; + STB__ARR(int) rescan=NULL; + remap = (int *) malloc(sizeof(remap[0]) * stb_arr_len(db->dirs)); + memset(remap, 0, sizeof(remap[0]) * stb_arr_len(db->dirs)); + rescan = NULL; + + for (i=0; i < stb_arr_len(db->dirs); ++i) { + struct _stat info; + if (0 == _stat(db->dirs[i].path, &info)) { + if (info.st_mode & _S_IFDIR) { + // it's still a directory, as expected + if (info.st_mtime > db->dirs[i].last_modified) { + // it's changed! force a rescan + // we don't want to scan it until we've stat()d its + // subdirs, though, so we queue it + stb_arr_push(rescan, i); + // update the last_mod time + db->dirs[i].last_modified = info.st_mtime; + // ignore existing files in this dir + remap[i] = -1; + changes_detected = TRUE; + } else { + // it hasn't changed, just copy it through unchanged + stb__dirtree_add_dir(db->dirs[i].path, db->dirs[i].last_modified, active); + remap[i] = stb_arr_lastn(active->dirs); + } + } else { + // this path used to refer to a directory, but now it's a file! + // assume that the parent directory is going to be forced to rescan anyway + goto delete_entry; + } + } else { + delete_entry: + // directory no longer exists, so don't copy it + // we don't free it because it's in the string pool now + db->dirs[i].path = NULL; + remap[i] = -1; + changes_detected = TRUE; + } + } + + // at this point, we have: + // + // holds a list of directory indices that need to be scanned due to being out of date + // holds the directory index in for each dir in , if it exists; -1 if not + // directories in are not in yet + + // so we can go ahead and remap all the known files right now + for (i=0; i < stb_arr_len(db->files); ++i) { + int dir = db->files[i].dir; + if (remap[dir] >= 0) { + stb__dirtree_add_file(db->files[i].name, remap[dir], db->files[i].size, db->files[i].last_modified, active); + } + } + + // at this point we're done with db->files, and done with remap + free(remap); + + // now scan those directories using the standard scan + for (i=0; i < stb_arr_len(rescan); ++i) { + int z = rescan[i]; + stb__dirtree_scandir(db->dirs[z].path, db->dirs[z].last_modified, active); + } + stb_arr_free(rescan); + + return changes_detected; +} + +static void stb__dirtree_free_raw(stb_dirtree *d) +{ + stb_free(d->string_pool); + stb_arr_free(d->dirs); + stb_arr_free(d->files); +} + +stb_dirtree *stb_dirtree_get_with_file(char *dir, char *cache_file) +{ + stb_dirtree *output = (stb_dirtree *) malloc(sizeof(*output)); + stb_dirtree db,active; + int prev_dir_count, cache_mismatch; + + char *stripped_dir; // store the directory name without a trailing '/' or '\\' + + // load the database of last-known state on disk + db.string_pool = NULL; + db.files = NULL; + db.dirs = NULL; + + stripped_dir = stb_strip_final_slash(strdup(dir)); + + if (cache_file != NULL) + stb__dirtree_load_db(cache_file, &db, stripped_dir); + + active.files = NULL; + active.dirs = NULL; + active.string_pool = stb_malloc(0,1); // @TODO: share string pools between both? + + // check all the directories in the database; make note if + // anything we scanned had changed, and rescan those things + cache_mismatch = stb__dirtree_update_db(&db, &active); + + // check the root tree + prev_dir_count = stb_arr_len(active.dirs); // record how many directories we've seen + + stb__dirtree_scandir(stripped_dir, 0, &active); // no last_modified time available for root + + // done with the DB; write it back out if any changes, i.e. either + // 1. any inconsistency found between cached information and actual disk + // or 2. if scanning the root found any new directories--which we detect because + // more than one directory got added to the active db during that scan + if (cache_mismatch || stb_arr_len(active.dirs) > prev_dir_count+1) + stb__dirtree_save_db(cache_file, &active, stripped_dir); + + free(stripped_dir); + + stb__dirtree_free_raw(&db); + + *output = active; + return output; +} + +stb_dirtree *stb_dirtree_get_dir(char *dir, char *cache_dir) +{ + int i; + uint8 sha[20]; + char dir_lower[1024]; + char cache_file[1024],*s; + if (cache_dir == NULL) + return stb_dirtree_get_with_file(dir, NULL); + strcpy(dir_lower, dir); + stb_tolower(dir_lower); + stb_sha1(sha, (unsigned char *) dir_lower, strlen(dir_lower)); + strcpy(cache_file, cache_dir); + s = cache_file + strlen(cache_file); + if (s[-1] != '//' && s[-1] != '\\') *s++ = '/'; + strcpy(s, "dirtree_"); + s += strlen(s); + for (i=0; i < 8; ++i) { + char *hex = "0123456789abcdef"; + uint z = sha[i]; + *s++ = hex[z >> 4]; + *s++ = hex[z & 15]; + } + strcpy(s, ".bin"); + return stb_dirtree_get_with_file(dir, cache_file); +} + +stb_dirtree *stb_dirtree_get(char *dir) +{ + char cache_dir[256]; + void *reg = stb_reg_open("rHKLM", "Software\\SilverSpaceship\\stb"); + strcpy(cache_dir, "c:/stb"); + if (reg) { + stb_reg_read(reg, "dirtree", cache_dir, sizeof(cache_dir)); + stb_reg_close(reg); + } + return stb_dirtree_get_dir(dir, cache_dir); +} + +void stb_dirtree_free(stb_dirtree *d) +{ + stb__dirtree_free_raw(d); + free(d); +} +#endif // STB_DEFINE + +#endif // _WIN32 +#endif // STB_NO_STB_STRINGS ////////////////////////////////////////////////////////////////////////////// // @@ -6715,6 +6974,154 @@ int stb_ps_eq(stb_ps *p0, stb_ps *p1) #endif +////////////////////////////////////////////////////////////////////////////// +// +// Random Numbers via Meresenne Twister or LCG +// + +STB_EXTERN unsigned long stb_srandLCG(unsigned long seed); +STB_EXTERN unsigned long stb_randLCG(void); +STB_EXTERN double stb_frandLCG(void); + +STB_EXTERN void stb_srand(unsigned long seed); +STB_EXTERN unsigned long stb_rand(void); +STB_EXTERN double stb_frand(void); +STB_EXTERN void stb_shuffle(void *p, size_t n, size_t sz, + unsigned long seed); +STB_EXTERN void stb_reverse(void *p, size_t n, size_t sz); + +STB_EXTERN unsigned long stb_randLCG_explicit(unsigned long seed); + +#define stb_rand_define(x,y) \ + \ + unsigned long x(void) \ + { \ + static unsigned long stb__rand = y; \ + stb__rand = stb__rand * 2147001325 + 715136305; /* BCPL */ \ + return 0x31415926 ^ ((stb__rand >> 16) + (stb__rand << 16)); \ + } + +#ifdef STB_DEFINE +unsigned long stb_randLCG_explicit(unsigned long seed) +{ + return seed * 2147001325 + 715136305; +} + +static unsigned long stb__rand_seed=0; + +unsigned long stb_srandLCG(unsigned long seed) +{ + unsigned long previous = stb__rand_seed; + stb__rand_seed = seed; + return previous; +} + +unsigned long stb_randLCG(void) +{ + stb__rand_seed = stb__rand_seed * 2147001325 + 715136305; // BCPL generator + // shuffle non-random bits to the middle, and xor to decorrelate with seed + return 0x31415926 ^ ((stb__rand_seed >> 16) + (stb__rand_seed << 16)); +} + +double stb_frandLCG(void) +{ + return stb_randLCG() / ((double) (1 << 16) * (1 << 16)); +} + +void stb_shuffle(void *p, size_t n, size_t sz, unsigned long seed) +{ + char *a; + unsigned long old_seed; + int i; + if (seed) + old_seed = stb_srandLCG(seed); + a = (char *) p + (n-1) * sz; + + for (i=n; i > 1; --i) { + int j = stb_randLCG() % i; + stb_swap(a, (char *) p + j * sz, sz); + a -= sz; + } + if (seed) + stb_srandLCG(old_seed); +} + +void stb_reverse(void *p, size_t n, size_t sz) +{ + int i,j = n-1; + for (i=0; i < j; ++i,--j) { + stb_swap((char *) p + i * sz, (char *) p + j * sz, sz); + } +} + +// public domain Mersenne Twister by Michael Brundage +#define STB__MT_LEN 624 + +int stb__mt_index = STB__MT_LEN*sizeof(unsigned long)+1; +unsigned long stb__mt_buffer[STB__MT_LEN]; + +void stb_srand(unsigned long seed) +{ + int i; + unsigned long old = stb_srandLCG(seed); + for (i = 0; i < STB__MT_LEN; i++) + stb__mt_buffer[i] = stb_randLCG(); + stb_srandLCG(old); + stb__mt_index = STB__MT_LEN*sizeof(unsigned long); +} + +#define STB__MT_IA 397 +#define STB__MT_IB (STB__MT_LEN - STB__MT_IA) +#define STB__UPPER_MASK 0x80000000 +#define STB__LOWER_MASK 0x7FFFFFFF +#define STB__MATRIX_A 0x9908B0DF +#define STB__TWIST(b,i,j) ((b)[i] & STB__UPPER_MASK) | ((b)[j] & STB__LOWER_MASK) +#define STB__MAGIC(s) (((s)&1)*STB__MATRIX_A) + +unsigned long stb_rand() +{ + unsigned long * b = stb__mt_buffer; + int idx = stb__mt_index; + unsigned long s,r; + int i; + + if (idx >= STB__MT_LEN*sizeof(unsigned long)) { + if (idx > STB__MT_LEN*sizeof(unsigned long)) + stb_srand(0); + idx = 0; + i = 0; + for (; i < STB__MT_IB; i++) { + s = STB__TWIST(b, i, i+1); + b[i] = b[i + STB__MT_IA] ^ (s >> 1) ^ STB__MAGIC(s); + } + for (; i < STB__MT_LEN-1; i++) { + s = STB__TWIST(b, i, i+1); + b[i] = b[i - STB__MT_IB] ^ (s >> 1) ^ STB__MAGIC(s); + } + + s = STB__TWIST(b, STB__MT_LEN-1, 0); + b[STB__MT_LEN-1] = b[STB__MT_IA-1] ^ (s >> 1) ^ STB__MAGIC(s); + } + stb__mt_index = idx + sizeof(unsigned long); + + r = *(unsigned long *)((unsigned char *)b + idx); + + r ^= (r >> 11); + r ^= (r << 7) & 0x9D2C5680; + r ^= (r << 15) & 0xEFC60000; + r ^= (r >> 18); + + return r; +} + +double stb_frand(void) +{ + return stb_rand() / ((double) (1 << 16) * (1 << 16)); +} + +#endif + + ////////////////////////////////////////////////////////////////////////////// // // stb_dupe @@ -6756,7 +7163,7 @@ int stb_ps_eq(stb_ps *p0, stb_ps *p1) typedef struct stb_dupe stb_dupe; typedef int (*stb_compare_func)(void *a, void *b); -typedef int (*stb_hash_func)(void *a); +typedef int (*stb_hash_func)(void *a, unsigned int seed); STB_EXTERN void stb_dupe_free(stb_dupe *sd); STB_EXTERN stb_dupe *stb_dupe_create(stb_hash_func hash, @@ -6834,7 +7241,7 @@ stb_dupe *stb_dupe_create(stb_hash_func hash, stb_compare_func eq, int size, void stb_dupe_add(stb_dupe *sd, void *item) { - uint32 hash = sd->hash(item) >> sd->hash_shift; + uint32 hash = sd->hash(item, sd->hash_shift); int z = hash & (sd->hash_size-1); stb_arr_push(sd->hash_table[z], item); ++sd->population; @@ -6878,7 +7285,7 @@ void stb_dupe_finish(stb_dupe *sd) // recursively process this row using stb_dupe, O(N log log N) stb_dupe *d = stb_dupe_create(sd->hash, sd->eq, n, sd->ineq); - d->hash_shift = sd->hash_shift + sd->size_log2; + d->hash_shift = stb_randLCG_explicit(sd->hash_shift); for (j=0; j < n; ++j) stb_dupe_add(d, list[j]); sd->hash_table[i] = stb_arr_free(sd->hash_table[i]); @@ -7066,147 +7473,6 @@ MODE FUNCNAME(TYPE *p, int n) \ STB_(FUNCNAME, _ins_sort)(p, n); \ } \ -////////////////////////////////////////////////////////////////////////////// -// -// Random Numbers via Meresenne Twister or LCG -// - -STB_EXTERN unsigned long stb_srandLCG(unsigned long seed); -STB_EXTERN unsigned long stb_randLCG(void); -STB_EXTERN double stb_frandLCG(void); - -STB_EXTERN void stb_srand(unsigned long seed); -STB_EXTERN unsigned long stb_rand(void); -STB_EXTERN double stb_frand(void); -STB_EXTERN void stb_shuffle(void *p, size_t n, size_t sz, - unsigned long seed); -STB_EXTERN void stb_reverse(void *p, size_t n, size_t sz); - -#define stb_rand_define(x,y) \ - \ - unsigned long x(void) \ - { \ - static unsigned long stb__rand = y; \ - stb__rand = stb__rand * 2147001325 + 715136305; /* BCPL */ \ - return 0x31415926 ^ ((stb__rand >> 16) + (stb__rand << 16)); \ - } - - -#ifdef STB_DEFINE -static unsigned long stb__rand_seed=0; - -unsigned long stb_srandLCG(unsigned long seed) -{ - unsigned long previous = stb__rand_seed; - stb__rand_seed = seed; - return previous; -} - -unsigned long stb_randLCG(void) -{ - stb__rand_seed = stb__rand_seed * 2147001325 + 715136305; // BCPL generator - // shuffle non-random bits to the middle, and xor to decorrelate with seed - return 0x31415926 ^ ((stb__rand_seed >> 16) + (stb__rand_seed << 16)); -} - -double stb_frandLCG(void) -{ - return stb_randLCG() / ((double) (1 << 16) * (1 << 16)); -} - -void stb_shuffle(void *p, size_t n, size_t sz, unsigned long seed) -{ - char *a; - unsigned long old_seed; - int i; - if (seed) - old_seed = stb_srandLCG(seed); - a = (char *) p + (n-1) * sz; - - for (i=n; i > 1; --i) { - int j = stb_randLCG() % i; - stb_swap(a, (char *) p + j * sz, sz); - a -= sz; - } - if (seed) - stb_srandLCG(old_seed); -} - -void stb_reverse(void *p, size_t n, size_t sz) -{ - int i,j = n-1; - for (i=0; i < j; ++i,--j) { - stb_swap((char *) p + i * sz, (char *) p + j * sz, sz); - } -} - -// public domain Mersenne Twister by Michael Brundage -#define STB__MT_LEN 624 - -int stb__mt_index = STB__MT_LEN*sizeof(unsigned long)+1; -unsigned long stb__mt_buffer[STB__MT_LEN]; - -void stb_srand(unsigned long seed) -{ - int i; - unsigned long old = stb_srandLCG(seed); - for (i = 0; i < STB__MT_LEN; i++) - stb__mt_buffer[i] = stb_randLCG(); - stb_srandLCG(old); - stb__mt_index = STB__MT_LEN*sizeof(unsigned long); -} - -#define STB__MT_IA 397 -#define STB__MT_IB (STB__MT_LEN - STB__MT_IA) -#define STB__UPPER_MASK 0x80000000 -#define STB__LOWER_MASK 0x7FFFFFFF -#define STB__MATRIX_A 0x9908B0DF -#define STB__TWIST(b,i,j) ((b)[i] & STB__UPPER_MASK) | ((b)[j] & STB__LOWER_MASK) -#define STB__MAGIC(s) (((s)&1)*STB__MATRIX_A) - -unsigned long stb_rand() -{ - unsigned long * b = stb__mt_buffer; - int idx = stb__mt_index; - unsigned long s,r; - int i; - - if (idx >= STB__MT_LEN*sizeof(unsigned long)) { - if (idx > STB__MT_LEN*sizeof(unsigned long)) - stb_srand(0); - idx = 0; - i = 0; - for (; i < STB__MT_IB; i++) { - s = STB__TWIST(b, i, i+1); - b[i] = b[i + STB__MT_IA] ^ (s >> 1) ^ STB__MAGIC(s); - } - for (; i < STB__MT_LEN-1; i++) { - s = STB__TWIST(b, i, i+1); - b[i] = b[i - STB__MT_IB] ^ (s >> 1) ^ STB__MAGIC(s); - } - - s = STB__TWIST(b, STB__MT_LEN-1, 0); - b[STB__MT_LEN-1] = b[STB__MT_IA-1] ^ (s >> 1) ^ STB__MAGIC(s); - } - stb__mt_index = idx + sizeof(unsigned long); - - r = *(unsigned long *)((unsigned char *)b + idx); - - r ^= (r >> 11); - r ^= (r << 7) & 0x9D2C5680; - r ^= (r << 15) & 0xEFC60000; - r ^= (r >> 18); - - return r; -} - -double stb_frand(void) -{ - return stb_rand() / ((double) (1 << 16) * (1 << 16)); -} - -#endif - ////////////////////////////////////////////////////////////////////////////// // @@ -8481,7 +8747,8 @@ typedef struct extern stb_info_struct stb__introspect_output[]; -STB_EXTERN void stb__introspect(char *path, char *file, stb_info_struct *compiled); +STB_EXTERN void stb_introspect_precompiled(stb_info_struct *compiled); +STB_EXTERN void stb__introspect(char *path, char *file); #define stb_introspect_ship() stb__introspect(NULL, NULL, stb__introspect_output) @@ -8489,11 +8756,11 @@ STB_EXTERN void stb__introspect(char *path, char *file, stb_info_struct *compile #define stb_introspect() stb_introspect_ship() #define stb_introspect_path(p) stb_introspect_ship() #else -// bootstrapping: define stb_introspect_bootstrap() the first time -#define stb_introspect_bootstrap() stb__introspect(NULL, __FILE__, NULL) -#define stb_introspect() stb__introspect(NULL, __FILE__, stb__introspect_output) +// bootstrapping: define stb_introspect() (or 'path') the first time +#define stb_introspect() stb__introspect(NULL, __FILE__, NULL) +#define stb_introspect_auto() stb__introspect(NULL, __FILE__, stb__introspect_output) -#define stb_introspect_path_bootstrap(p) stb__introspect(p, __FILE__, NULL) +#define stb_introspect_path(p) stb__introspect(p, __FILE__, NULL) #define stb_introspect_path(p) stb__introspect(p, __FILE__, NULL) #endif @@ -8507,6 +8774,12 @@ STB_EXTERN void stb__introspect(char *path, char *file, stb_info_struct *compile #endif #endif +void stb_introspect_precompiled(stb_info_struct *compiled) +{ + +} + + static void stb__introspect_filename(char *buffer, char *path) { #if STB_INTROSPECT_CPP @@ -8525,14 +8798,14 @@ static void stb__introspect_compute(char *path, char *file) f = fopen(file, "w"); if (!f) return; - fputs("// if you get compiler errors, uncomment the following line:\n", f); - fputs("//#define STB_INTROSPECT_INVALID\n\n", f); + fputs("// if you get compiler errors, change the following 0 to a 1:\n", f); + fputs("#define STB_INTROSPECT_INVALID 0\n\n", f); fputs("// this will force the code to compile, and force the introspector\n", f); fputs("// to run and then exit, allowing you to recompile\n\n\n", f); - fputs("#include \"stb.h\"\n",f ); - fputs("#ifdef STB_INTROSPECT_INVALID\n", f); + fputs("#include \"stb.h\"\n\n",f ); + fputs("#if STB_INTROSPECT_INVALID\n", f); fputs(" stb_info_struct stb__introspect_output[] = { (void *) 1 }\n", f); - fputs("#else\n", f); + fputs("#else\n\n", f); for (i=0; i < stb_arr_len(include_list); ++i) fprintf(f, " #include \"%s\"\n", include_list[i]); @@ -9342,6 +9615,12 @@ void stb_compress_stream_end(int close) #endif // STB_DEFINE + +#ifndef WIN32 +#define STB_NO_THREADS +#endif + +#ifndef STB_NO_THREADS ////////////////////////////////////////////////////////////////////////////// // // Threads @@ -9364,6 +9643,8 @@ typedef struct stb__sync *stb_sync; // get the number of processors (limited to those in the affinity mask for this process). STB_EXTERN int stb_processor_count(void); +// force to run on a single core -- needed for RDTSC to work, e.g. for iprof +STB_EXTERN void stb_force_uniprocessor(void); // stb_work functions: queue up work to be done by some worker threads @@ -9543,6 +9824,7 @@ static void stb__thread_sleep(int ms) { Sleep(ms); } #ifndef _WINDOWS_ STB__IMPORT int __stdcall GetProcessAffinityMask(void *, STB__DW *, STB__DW *); STB__IMPORT void * __stdcall GetCurrentProcess(void); +STB__IMPORT int __stdcall SetProcessAffinityMask(void *, STB__DW); #endif int stb_processor_count(void) @@ -9552,6 +9834,22 @@ int stb_processor_count(void) return stb_bitcount(proc); } +void stb_force_uniprocessor(void) +{ + unsigned long proc,sys; + GetProcessAffinityMask(GetCurrentProcess(), &proc, &sys); + if (stb_bitcount(proc) > 1) { + int z; + for (z=0; z < 32; ++z) + if (proc & (1 << z)) + break; + if (z < 32) { + proc = 1 << z; + SetProcessAffinityMask(GetCurrentProcess(), proc); + } + } +} + #ifdef _WINDOWS_ #define STB_MUTEX_NATIVE DWORD foob; @@ -10030,7 +10328,7 @@ static void stb_work_init(int num_threads) stb__threadmutex_init(); stb_mutex_begin(stb__workmutex); stb_barrier(); - if ((stb_workqueue * volatile) stb__work_global == NULL) + if (*(stb_workqueue * volatile *) &stb__work_global == NULL) stb__work_global = stb_workq_new(num_threads, stb__work_maxitems); stb_mutex_end(stb__workmutex); } @@ -10154,7 +10452,7 @@ void stb__io_init(void) stb__threadmutex_init(); stb_mutex_begin(stb__threadmutex); stb_barrier(); - if ((stb_thread * volatile) stb__diskio == NULL) { + if (*(stb_thread * volatile *) &stb__diskio == NULL) { stb__diskio_mutex = stb_mutex_new(); stb__diskio = stb_workq_new_flags(1,STB__MAX_DISK_COMMAND,TRUE,TRUE); // no remove mutex } @@ -10256,6 +10554,9 @@ int stb_bgio_readf_to(FILE *f, int offset, int len, stb_uchar *buffer, int *olen return stb__io_add(NULL,f,offset,len,buffer,NULL,olen,NULL); } #endif +#endif + + ////////////////////////////////////////////////////////////////////////////// // diff --git a/stb_image.c b/stb_image.c index ebfc643..a01fda5 100644 --- a/stb_image.c +++ b/stb_image.c @@ -1,4 +1,4 @@ -/* stbi-1.02 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c +/* stbi-1.15 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c when you control the images you're loading QUICK NOTES: @@ -9,15 +9,30 @@ PNG non-interlaced BMP non-1bpp, non-RLE TGA (not sure what subset, if a subset) + PSD (composited view only, no extra channels) HDR (radiance rgbE format) writes BMP,TGA (define STBI_NO_WRITE to remove code) decoded from memory or through stdio FILE (define STBI_NO_STDIO to remove code) + supports installable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD) TODO: stbi_info_* - PSD loader history: + 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 @@ -48,6 +63,9 @@ */ +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + //// begin header file //////////////////////////////////////////////////// // // Limitations: @@ -136,15 +154,11 @@ // // stbi_is_hdr(char *filename); - #ifndef STBI_NO_STDIO #include #endif -#ifndef STBI_NO_HDR -#include // ldexp -#include // strcmp -#endif +#define STBI_VERSION 1 enum { @@ -168,27 +182,27 @@ extern "C" { // write a BMP/TGA file given tightly packed 'comp' channels (no padding, nor bmp-stride-padding) // (you must include the appropriate extension in the filename). // returns TRUE on success, FALSE if couldn't open file, error writing file -extern int stbi_write_bmp (char *filename, int x, int y, int comp, void *data); -extern int stbi_write_tga (char *filename, int x, int y, int comp, void *data); +extern int stbi_write_bmp (char const *filename, int x, int y, int comp, void *data); +extern int stbi_write_tga (char const *filename, int x, int y, int comp, void *data); #endif // PRIMARY API - works on images of any type // load image by filename, open file, or memory buffer #ifndef STBI_NO_STDIO -extern stbi_uc *stbi_load (char *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); extern stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); #endif -extern stbi_uc *stbi_load_from_memory(stbi_uc *buffer, int len, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); // for stbi_load_from_file, file pointer is left pointing immediately after image #ifndef STBI_NO_HDR #ifndef STBI_NO_STDIO -extern float *stbi_loadf (char *filename, int *x, int *y, int *comp, int req_comp); +extern float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); extern float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); #endif -extern float *stbi_loadf_from_memory(stbi_uc *buffer, int len, int *x, int *y, int *comp, int req_comp); +extern float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); extern void stbi_hdr_to_ldr_gamma(float gamma); extern void stbi_hdr_to_ldr_scale(float scale); @@ -199,86 +213,94 @@ extern void stbi_ldr_to_hdr_scale(float scale); #endif // STBI_NO_HDR // get a VERY brief reason for failure -extern char *stbi_failure_reason (void); +// NOT THREADSAFE +extern char *stbi_failure_reason (void); // free the loaded image -- this is just free() -extern void stbi_image_free (stbi_uc *retval_from_stbi_load); +extern void stbi_image_free (void *retval_from_stbi_load); // get image dimensions & components without fully decoding -extern int stbi_info_from_memory(stbi_uc *buffer, int len, int *x, int *y, int *comp); -extern int stbi_is_hdr_from_memory(stbi_uc *buffer, int len); +extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); #ifndef STBI_NO_STDIO -extern int stbi_info (char *filename, int *x, int *y, int *comp); -extern int stbi_is_hdr (char *filename); +extern int stbi_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_is_hdr (char const *filename); extern int stbi_is_hdr_from_file(FILE *f); #endif // ZLIB client - used by PNG, available for other purposes -extern char *stbi_zlib_decode_malloc_guesssize(int initial_size, int *outlen); -extern char *stbi_zlib_decode_malloc(char *buffer, int len, int *outlen); -extern int stbi_zlib_decode_buffer(char *obuffer, int olen, char *ibuffer, int ilen); - -extern char *stbi_zlib_decode_noheader_malloc(char *buffer, int len, int *outlen); -extern int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, char *ibuffer, int ilen); +extern char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +extern char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); +extern char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); // TYPE-SPECIFIC ACCESS // is it a jpeg? -extern int stbi_jpeg_test_memory (stbi_uc *buffer, int len); -extern stbi_uc *stbi_jpeg_load_from_memory(stbi_uc *buffer, int len, int *x, int *y, int *comp, int req_comp); -extern int stbi_jpeg_info_from_memory(stbi_uc *buffer, int len, int *x, int *y, int *comp); +extern int stbi_jpeg_test_memory (stbi_uc const *buffer, int len); +extern stbi_uc *stbi_jpeg_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +extern int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); #ifndef STBI_NO_STDIO -extern stbi_uc *stbi_jpeg_load (char *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_jpeg_load (char const *filename, int *x, int *y, int *comp, int req_comp); extern int stbi_jpeg_test_file (FILE *f); extern stbi_uc *stbi_jpeg_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); -extern int stbi_jpeg_info (char *filename, int *x, int *y, int *comp); +extern int stbi_jpeg_info (char const *filename, int *x, int *y, int *comp); extern int stbi_jpeg_info_from_file (FILE *f, int *x, int *y, int *comp); #endif -extern int stbi_jpeg_dc_only; // only decode DC component - // is it a png? -extern int stbi_png_test_memory (stbi_uc *buffer, int len); -extern stbi_uc *stbi_png_load_from_memory (stbi_uc *buffer, int len, int *x, int *y, int *comp, int req_comp); -extern int stbi_png_info_from_memory (stbi_uc *buffer, int len, int *x, int *y, int *comp); +extern int stbi_png_test_memory (stbi_uc const *buffer, int len); +extern stbi_uc *stbi_png_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +extern int stbi_png_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp); #ifndef STBI_NO_STDIO -extern stbi_uc *stbi_png_load (char *filename, int *x, int *y, int *comp, int req_comp); -extern int stbi_png_info (char *filename, int *x, int *y, int *comp); +extern stbi_uc *stbi_png_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern int stbi_png_info (char const *filename, int *x, int *y, int *comp); extern int stbi_png_test_file (FILE *f); extern stbi_uc *stbi_png_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); extern int stbi_png_info_from_file (FILE *f, int *x, int *y, int *comp); #endif // is it a bmp? -extern int stbi_bmp_test_memory (stbi_uc *buffer, int len); +extern int stbi_bmp_test_memory (stbi_uc const *buffer, int len); -extern stbi_uc *stbi_bmp_load (char *filename, int *x, int *y, int *comp, int req_comp); -extern stbi_uc *stbi_bmp_load_from_memory (stbi_uc *buffer, int len, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_bmp_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_bmp_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); #ifndef STBI_NO_STDIO extern int stbi_bmp_test_file (FILE *f); extern stbi_uc *stbi_bmp_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); #endif // is it a tga? -extern int stbi_tga_test_memory (stbi_uc *buffer, int len); +extern int stbi_tga_test_memory (stbi_uc const *buffer, int len); -extern stbi_uc *stbi_tga_load (char *filename, int *x, int *y, int *comp, int req_comp); -extern stbi_uc *stbi_tga_load_from_memory (stbi_uc *buffer, int len, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_tga_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_tga_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); #ifndef STBI_NO_STDIO extern int stbi_tga_test_file (FILE *f); extern stbi_uc *stbi_tga_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); #endif -// is it an hdr? -extern int stbi_hdr_test_memory (stbi_uc *buffer, int len); +// is it a psd? +extern int stbi_psd_test_memory (stbi_uc const *buffer, int len); -extern float * stbi_hdr_load (char *filename, int *x, int *y, int *comp, int req_comp); -extern float * stbi_hdr_load_from_memory (stbi_uc *buffer, int len, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_psd_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_psd_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_psd_test_file (FILE *f); +extern stbi_uc *stbi_psd_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// is it an hdr? +extern int stbi_hdr_test_memory (stbi_uc const *buffer, int len); + +extern float * stbi_hdr_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern float * stbi_hdr_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); #ifndef STBI_NO_STDIO extern int stbi_hdr_test_file (FILE *f); extern float * stbi_hdr_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); @@ -287,8 +309,8 @@ extern float * stbi_hdr_load_from_file (FILE *f, int *x, int // define new loaders typedef struct { - int (*test_memory)(stbi_uc *buffer, int len); - stbi_uc * (*load_from_memory)(stbi_uc *buffer, int len, int *x, int *y, int *comp, int req_comp); + int (*test_memory)(stbi_uc const *buffer, int len); + stbi_uc * (*load_from_memory)(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); #ifndef STBI_NO_STDIO int (*test_file)(FILE *f); stbi_uc * (*load_from_file)(FILE *f, int *x, int *y, int *comp, int req_comp); @@ -297,8 +319,28 @@ typedef struct // register a loader by filling out the above structure (you must defined ALL functions) // returns 1 if added or already added, 0 if not added (too many loaders) +// NOT THREADSAFE extern int stbi_register_loader(stbi_loader *loader); +// define faster low-level operations (typically SIMD support) +#if STBI_SIMD +typedef void (*stbi_idct_8x8)(uint8 *out, int out_stride, short data[64], unsigned short *dequantize); +// compute an integer IDCT on "input" +// input[x] = data[x] * dequantize[x] +// write results to 'out': 64 samples, each run of 8 spaced by 'out_stride' +// CLAMP results to 0..255 +typedef void (*stbi_YCbCr_to_RGB_run)(uint8 *output, uint8 const *y, uint8 const *cb, uint8 const *cr, int count, int step); +// compute a conversion from YCbCr to RGB +// 'count' pixels +// write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B +// y: Y input channel +// cb: Cb input channel; scale/biased to be 0..255 +// cr: Cr input channel; scale/biased to be 0..255 + +extern void stbi_install_idct(stbi_idct_8x8 func); +extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func); +#endif // STBI_SIMD + #ifdef __cplusplus } #endif @@ -306,6 +348,14 @@ extern int stbi_register_loader(stbi_loader *loader); // // //// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifndef STBI_HEADER_FILE_ONLY + +#ifndef STBI_NO_HDR +#include // ldexp +#include // strcmp +#endif #ifndef STBI_NO_STDIO #include @@ -316,9 +366,14 @@ extern int stbi_register_loader(stbi_loader *loader); #include #ifndef _MSC_VER -#define __forceinline + #ifdef __cplusplus + #define __forceinline inline + #else + #define __forceinline + #endif #endif + // implementation: typedef unsigned char uint8; typedef unsigned short uint16; @@ -339,6 +394,7 @@ typedef unsigned char validate_uint32[sizeof(uint32)==4]; // Generic API that works on all image types // +// this is not threadsafe static char *failure_reason; char *stbi_failure_reason(void) @@ -360,9 +416,10 @@ static int e(char *str) #define e(x,y) e(x) #endif -#define ep(x,y) (e(x,y)?NULL:NULL) +#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(unsigned char *retval_from_stbi_load) +void stbi_image_free(void *retval_from_stbi_load) { free(retval_from_stbi_load); } @@ -395,11 +452,11 @@ static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp); #endif #ifndef STBI_NO_STDIO -unsigned char *stbi_load(char *filename, int *x, int *y, int *comp, int req_comp) +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 ep("can't fopen", "Unable to open file"); + 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; @@ -414,6 +471,8 @@ unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_c return stbi_png_load_from_file(f,x,y,comp,req_comp); if (stbi_bmp_test_file(f)) return stbi_bmp_load_from_file(f,x,y,comp,req_comp); + if (stbi_psd_test_file(f)) + return stbi_psd_load_from_file(f,x,y,comp,req_comp); #ifndef STBI_NO_HDR if (stbi_hdr_test_file(f)) { float *hdr = stbi_hdr_load_from_file(f, x,y,comp,req_comp); @@ -426,11 +485,11 @@ unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_c // test tga last because it's a crappy test! if (stbi_tga_test_file(f)) return stbi_tga_load_from_file(f,x,y,comp,req_comp); - return ep("unknown image type", "Image not of any known type, or corrupt"); + return epuc("unknown image type", "Image not of any known type, or corrupt"); } #endif -unsigned char *stbi_load_from_memory(stbi_uc *buffer, int len, int *x, int *y, int *comp, int req_comp) +unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { int i; if (stbi_jpeg_test_memory(buffer,len)) @@ -439,6 +498,8 @@ unsigned char *stbi_load_from_memory(stbi_uc *buffer, int len, int *x, int *y, i return stbi_png_load_from_memory(buffer,len,x,y,comp,req_comp); if (stbi_bmp_test_memory(buffer,len)) return stbi_bmp_load_from_memory(buffer,len,x,y,comp,req_comp); + if (stbi_psd_test_memory(buffer,len)) + return stbi_psd_load_from_memory(buffer,len,x,y,comp,req_comp); #ifndef STBI_NO_HDR if (stbi_hdr_test_memory(buffer, len)) { float *hdr = stbi_hdr_load_from_memory(buffer, len,x,y,comp,req_comp); @@ -451,17 +512,17 @@ unsigned char *stbi_load_from_memory(stbi_uc *buffer, int len, int *x, int *y, i // test tga last because it's a crappy test! if (stbi_tga_test_memory(buffer,len)) return stbi_tga_load_from_memory(buffer,len,x,y,comp,req_comp); - return ep("unknown image type", "Image not of any known type, or corrupt"); + return epuc("unknown image type", "Image not of any known type, or corrupt"); } #ifndef STBI_NO_HDR #ifndef STBI_NO_STDIO -float *stbi_loadf(char *filename, int *x, int *y, int *comp, int req_comp) +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 ep("can't fopen", "Unable to open file"); + 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; @@ -470,24 +531,28 @@ float *stbi_loadf(char *filename, int *x, int *y, int *comp, int req_comp) float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) { unsigned char *data; + #ifndef STBI_NO_HDR if (stbi_hdr_test_file(f)) return stbi_hdr_load_from_file(f,x,y,comp,req_comp); + #endif data = stbi_load_from_file(f, x, y, comp, req_comp); if (data) return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return ep("unknown image type", "Image not of any known type, or corrupt"); + return epf("unknown image type", "Image not of any known type, or corrupt"); } #endif -float *stbi_loadf_from_memory(stbi_uc *buffer, int len, int *x, int *y, int *comp, int req_comp) +float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { stbi_uc *data; + #ifndef STBI_NO_HDR if (stbi_hdr_test_memory(buffer, len)) return stbi_hdr_load_from_memory(buffer, len,x,y,comp,req_comp); + #endif data = stbi_load_from_memory(buffer, len, x, y, comp, req_comp); if (data) return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return ep("unknown image type", "Image not of any known type, or corrupt"); + return epf("unknown image type", "Image not of any known type, or corrupt"); } #endif @@ -495,13 +560,17 @@ float *stbi_loadf_from_memory(stbi_uc *buffer, int len, int *x, int *y, int *com // defined, for API simplicity; if STBI_NO_HDR is defined, it always // reports false! -extern int stbi_is_hdr_from_memory(stbi_uc *buffer, int len) +int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) { + #ifndef STBI_NO_HDR return stbi_hdr_test_memory(buffer, len); + #else + return 0; + #endif } #ifndef STBI_NO_STDIO -extern int stbi_is_hdr (char *filename) +extern int stbi_is_hdr (char const *filename) { FILE *f = fopen(filename, "rb"); int result=0; @@ -514,15 +583,21 @@ extern int stbi_is_hdr (char *filename) extern int stbi_is_hdr_from_file(FILE *f) { + #ifndef STBI_NO_HDR return stbi_hdr_test_file(f); + #else + return 0; + #endif } #endif // @TODO: get image dimensions & components without fully decoding -extern int stbi_info (char *filename, int *x, int *y, int *comp); +#ifndef STBI_NO_STDIO +extern int stbi_info (char const *filename, int *x, int *y, int *comp); extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); -extern int stbi_info_from_memory(stbi_uc *buffer, int len, int *x, int *y, int *comp); +#endif +extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); #ifndef STBI_NO_HDR static float h2l_gamma_i=1.0f/2.2f, h2l_scale_i=1.0f; @@ -541,10 +616,6 @@ void stbi_ldr_to_hdr_scale(float scale) { l2h_scale = scale; } // Common code used by all image loaders // -// image width, height, # components -static uint32 img_x, img_y; -static int img_n, img_out_n; - enum { SCAN_load=0, @@ -552,99 +623,104 @@ enum SCAN_header, }; -// An API for reading either from memory or file. -#ifndef STBI_NO_STDIO -static FILE *img_file; -#endif -static uint8 *img_buffer, *img_buffer_end; +typedef struct +{ + uint32 img_x, img_y; + int img_n, img_out_n; + + #ifndef STBI_NO_STDIO + FILE *img_file; + #endif + uint8 *img_buffer, *img_buffer_end; +} stbi; #ifndef STBI_NO_STDIO -static void start_file(FILE *f) +static void start_file(stbi *s, FILE *f) { - img_file = f; + s->img_file = f; } #endif -static void start_mem(uint8 *buffer, int len) +static void start_mem(stbi *s, uint8 const *buffer, int len) { #ifndef STBI_NO_STDIO - img_file = NULL; + s->img_file = NULL; #endif - img_buffer = buffer; - img_buffer_end = buffer+len; + s->img_buffer = (uint8 *) buffer; + s->img_buffer_end = (uint8 *) buffer+len; } -static int get8(void) +__forceinline static int get8(stbi *s) { #ifndef STBI_NO_STDIO - if (img_file) { - int c = fgetc(img_file); + if (s->img_file) { + int c = fgetc(s->img_file); return c == EOF ? 0 : c; } #endif - if (img_buffer < img_buffer_end) - return *img_buffer++; + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; return 0; } -static int at_eof(void) +__forceinline static int at_eof(stbi *s) { #ifndef STBI_NO_STDIO - if (img_file) - return feof(img_file); + if (s->img_file) + return feof(s->img_file); #endif - return img_buffer >= img_buffer_end; + return s->img_buffer >= s->img_buffer_end; } -static uint8 get8u(void) +__forceinline static uint8 get8u(stbi *s) { - return (uint8) get8(); + return (uint8) get8(s); } -static void skip(int n) +static void skip(stbi *s, int n) { #ifndef STBI_NO_STDIO - if (img_file) - fseek(img_file, n, SEEK_CUR); + if (s->img_file) + fseek(s->img_file, n, SEEK_CUR); else #endif - img_buffer += n; + s->img_buffer += n; } -static int get16(void) +static int get16(stbi *s) { - int z = get8(); - return (z << 8) + get8(); + int z = get8(s); + return (z << 8) + get8(s); } -static uint32 get32(void) +static uint32 get32(stbi *s) { - uint32 z = get16(); - return (z << 16) + get16(); + uint32 z = get16(s); + return (z << 16) + get16(s); } -static int get16le(void) +static int get16le(stbi *s) { - int z = get8(); - return z + (get8() << 8); + int z = get8(s); + return z + (get8(s) << 8); } -static uint32 get32le(void) +static uint32 get32le(stbi *s) { - uint32 z = get16le(); - return z + (get16le() << 16); + uint32 z = get16le(s); + return z + (get16le(s) << 16); } -static void getn(stbi_uc *buffer, int n) +static void getn(stbi *s, stbi_uc *buffer, int n) { #ifndef STBI_NO_STDIO - if (img_file) { - fread(buffer, 1, n, img_file); + if (s->img_file) { + fread(buffer, 1, n, s->img_file); return; } #endif - memcpy(buffer, img_buffer, n); - img_buffer += n; + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; } ////////////////////////////////////////////////////////////////////////////// @@ -663,27 +739,26 @@ static uint8 compute_y(int r, int g, int b) return (uint8) (((r*77) + (g*150) + (29*b)) >> 8); } -static unsigned char *convert_format(unsigned char *data, int img_n, int req_comp) +static unsigned char *convert_format(unsigned char *data, int img_n, int req_comp, uint x, uint y) { - uint i,j; + 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 * img_x * img_y); + good = (unsigned char *) malloc(req_comp * x * y); if (good == NULL) { free(data); - return ep("outofmem", "Out of memory"); + return epuc("outofmem", "Out of memory"); } - for (j=0; j < img_y; ++j) { - unsigned char *src = data + j * img_x * img_n ; - unsigned char *dest = good + j * img_x * req_comp; + 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=0; i < img_x; ++i, src += a, dest += 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)) { @@ -705,20 +780,20 @@ static unsigned char *convert_format(unsigned char *data, int img_n, int req_com } free(data); - img_out_n = req_comp; 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 ep("outofmem", "Out of memory"); } + 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.0, l2h_gamma) * l2h_scale; + 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; } @@ -731,7 +806,7 @@ 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 ep("outofmem", "Out of memory"); } + 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) { @@ -751,6 +826,7 @@ static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp) free(data); return output; } +#endif ////////////////////////////////////////////////////////////////////////////// // @@ -779,8 +855,6 @@ static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp) // IJG 1998: 0.98 seconds (MSVC6, makefile provided by IJG) // IJG 1998: 0.95 seconds (MSVC6, makefile + proc=PPro) -int stbi_jpeg_dc_only; - // huffman decoding acceleration #define FAST_BITS 9 // larger handles more cases; smaller stomps less cache @@ -795,9 +869,44 @@ typedef struct int delta[17]; // old 'firstsymbol' - old 'firstcode' } huffman; -static huffman huff_dc[4]; // baseline is 2 tables, extended is 4 -static huffman huff_ac[4]; -static uint8 dequant[4][64]; +typedef struct +{ + #if STBI_SIMD + unsigned short dequant2[4][64]; + #endif + stbi s; + huffman huff_dc[4]; + huffman huff_ac[4]; + uint8 dequant[4][64]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + uint8 *data; + void *raw_data; + uint8 *linebuf; + } img_comp[4]; + + uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int scan_n, order[4]; + int restart_interval, todo; +} jpeg; static int build_huffman(huffman *h, int *count) { @@ -840,65 +949,42 @@ static int build_huffman(huffman *h, int *count) return 1; } -// sizes for components, interleaved MCUs -static int img_h_max, img_v_max; -static int img_mcu_x, img_mcu_y; -static int img_mcu_w, img_mcu_h; - -// definition of jpeg image component -static struct -{ - int id; - int h,v; - int tq; - int hd,ha; - int dc_pred; - - int x,y,w2,h2; - uint8 *data; -} img_comp[4]; - -static unsigned long code_buffer; // jpeg entropy-coded buffer -static int code_bits; // number of valid bits -static unsigned char marker; // marker seen while filling entropy buffer -static int nomore; // flag if we saw a marker so must stop - -static void grow_buffer_unsafe(void) +static void grow_buffer_unsafe(jpeg *j) { do { - int b = nomore ? 0 : get8(); + int b = j->nomore ? 0 : get8(&j->s); if (b == 0xff) { - int c = get8(); + int c = get8(&j->s); if (c != 0) { - marker = (unsigned char) c; - nomore = 1; + j->marker = (unsigned char) c; + j->nomore = 1; return; } } - code_buffer = (code_buffer << 8) | b; - code_bits += 8; - } while (code_bits <= 24); + j->code_buffer = (j->code_buffer << 8) | b; + j->code_bits += 8; + } while (j->code_bits <= 24); } // (1 << n) - 1 -static unsigned long bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; +static uint32 bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; // decode a jpeg huffman value from the bitstream -__forceinline static int decode(huffman *h) +__forceinline static int decode(jpeg *j, huffman *h) { unsigned int temp; int c,k; - if (code_bits < 16) grow_buffer_unsafe(); + if (j->code_bits < 16) grow_buffer_unsafe(j); // look at the top FAST_BITS and determine what symbol ID it is, // if the code is <= FAST_BITS - c = (code_buffer >> (code_bits - FAST_BITS)) & ((1 << FAST_BITS)-1); + c = (j->code_buffer >> (j->code_bits - FAST_BITS)) & ((1 << FAST_BITS)-1); k = h->fast[c]; if (k < 255) { - if (h->size[k] > code_bits) + if (h->size[k] > j->code_bits) return -1; - code_bits -= h->size[k]; + j->code_bits -= h->size[k]; return h->values[k]; } @@ -908,40 +994,40 @@ __forceinline static int decode(huffman *h) // end; in other words, regardless of the number of bits, it // wants to be compared against something shifted to have 16; // that way we don't need to shift inside the loop. - if (code_bits < 16) - temp = (code_buffer << (16 - code_bits)) & 0xffff; + if (j->code_bits < 16) + temp = (j->code_buffer << (16 - j->code_bits)) & 0xffff; else - temp = (code_buffer >> (code_bits - 16)) & 0xffff; + temp = (j->code_buffer >> (j->code_bits - 16)) & 0xffff; for (k=FAST_BITS+1 ; ; ++k) if (temp < h->maxcode[k]) break; if (k == 17) { // error! code not found - code_bits -= 16; + j->code_bits -= 16; return -1; } - if (k > code_bits) + if (k > j->code_bits) return -1; // convert the huffman code to the symbol id - c = ((code_buffer >> (code_bits - k)) & bmask[k]) + h->delta[k]; - assert((((code_buffer) >> (code_bits - h->size[c])) & bmask[h->size[c]]) == h->code[c]); + c = ((j->code_buffer >> (j->code_bits - k)) & bmask[k]) + h->delta[k]; + assert((((j->code_buffer) >> (j->code_bits - h->size[c])) & bmask[h->size[c]]) == h->code[c]); // convert the id to a symbol - code_bits -= k; + j->code_bits -= k; return h->values[c]; } // combined JPEG 'receive' and JPEG 'extend', since baseline // always extends everything it receives. -__forceinline static int extend_receive(int n) +__forceinline static int extend_receive(jpeg *j, int n) { unsigned int m = 1 << (n-1); unsigned int k; - if (code_bits < n) grow_buffer_unsafe(); - k = (code_buffer >> (code_bits - n)) & bmask[n]; - code_bits -= n; + if (j->code_bits < n) grow_buffer_unsafe(j); + k = (j->code_buffer >> (j->code_bits - n)) & bmask[n]; + j->code_bits -= n; // the following test is probably a random branch that won't // predict well. I tried to table accelerate it but failed. // maybe it's compiling as a conditional move? @@ -969,25 +1055,25 @@ static uint8 dezigzag[64+15] = }; // decode one 64-entry block-- -static int decode_block(short data[64], huffman *hdc, huffman *hac, int b) +static int decode_block(jpeg *j, short data[64], huffman *hdc, huffman *hac, int b) { int diff,dc,k; - int t = decode(hdc); + int t = decode(j, hdc); if (t < 0) return e("bad huffman code","Corrupt JPEG"); // 0 all the ac values now so we can do it 32-bits at a time memset(data,0,64*sizeof(data[0])); - diff = t ? extend_receive(t) : 0; - dc = img_comp[b].dc_pred + diff; - img_comp[b].dc_pred = dc; + diff = t ? extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; data[0] = (short) dc; // decode AC components, see JPEG spec k = 1; do { int r,s; - int rs = decode(hac); + int rs = decode(j, hac); if (rs < 0) return e("bad huffman code","Corrupt JPEG"); s = rs & 15; r = rs >> 4; @@ -997,7 +1083,7 @@ static int decode_block(short data[64], huffman *hdc, huffman *hac, int b) } else { k += r; // decode into unzigzag'd location - data[dezigzag[k++]] = (short) extend_receive(s); + data[dezigzag[k++]] = (short) extend_receive(j,s); } } while (k < 64); return 1; @@ -1056,6 +1142,7 @@ __forceinline static uint8 clamp(int x) t1 += p2+p4; \ t0 += p1+p3; +#if !STBI_SIMD // .344 seconds on 3*anemones.jpg static void idct_block(uint8 *out, int out_stride, short data[64], uint8 *dequantize) { @@ -1063,16 +1150,6 @@ static void idct_block(uint8 *out, int out_stride, short data[64], uint8 *dequan uint8 *o,*dq = dequantize; short *d = data; - if (stbi_jpeg_dc_only) { - // ok, I don't really know why this is right, but it seems to be: - int z = 128 + ((d[0] * dq[0]) >> 3); - for (i=0; i < 8; ++i) { - out[0] = out[1] = out[2] = out[3] = out[4] = out[5] = out[6] = out[7] = z; - out += out_stride; - } - return; - } - // columns for (i=0; i < 8; ++i,++d,++dq, ++v) { // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing @@ -1118,96 +1195,165 @@ static void idct_block(uint8 *out, int out_stride, short data[64], uint8 *dequan o[4] = clamp((x3-t0) >> 17); } } +#else +static void idct_block(uint8 *out, int out_stride, short data[64], unsigned short *dequantize) +{ + int i,val[64],*v=val; + uint8 *o; + unsigned short *dq = dequantize; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d,++dq, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] * dq[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24], + d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + x0 += 65536; x1 += 65536; x2 += 65536; x3 += 65536; + o[0] = clamp((x0+t3) >> 17); + o[7] = clamp((x0-t3) >> 17); + o[1] = clamp((x1+t2) >> 17); + o[6] = clamp((x1-t2) >> 17); + o[2] = clamp((x2+t1) >> 17); + o[5] = clamp((x2-t1) >> 17); + o[3] = clamp((x3+t0) >> 17); + o[4] = clamp((x3-t0) >> 17); + } +} +static stbi_idct_8x8 stbi_idct_installed = idct_block; + +extern void stbi_install_idct(stbi_idct_8x8 func) +{ + stbi_idct_installed = func; +} +#endif #define MARKER_none 0xff // if there's a pending marker from the entropy stream, return that // otherwise, fetch from the stream and get a marker. if there's no // marker, return 0xff, which is never a valid marker value -static uint8 get_marker(void) +static uint8 get_marker(jpeg *j) { uint8 x; - if (marker != MARKER_none) { x = marker; marker = MARKER_none; return x; } - x = get8u(); + if (j->marker != MARKER_none) { x = j->marker; j->marker = MARKER_none; return x; } + x = get8u(&j->s); if (x != 0xff) return MARKER_none; while (x == 0xff) - x = get8u(); + x = get8u(&j->s); return x; } // in each scan, we'll have scan_n components, and the order // of the components is specified by order[] -static int scan_n, order[4]; -static int restart_interval, todo; #define RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) // after a restart interval, reset the entropy decoder and // the dc prediction -static void reset(void) +static void reset(jpeg *j) { - code_bits = 0; - code_buffer = 0; - nomore = 0; - img_comp[0].dc_pred = img_comp[1].dc_pred = img_comp[2].dc_pred = 0; - marker = MARKER_none; - todo = restart_interval ? restart_interval : 0x7fffffff; + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; + j->marker = MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, // since we don't even allow 1<<30 pixels } -static int parse_entropy_coded_data(void) +static int parse_entropy_coded_data(jpeg *z) { - reset(); - if (scan_n == 1) { + reset(z); + if (z->scan_n == 1) { int i,j; + #if STBI_SIMD + __declspec(align(16)) + #endif short data[64]; - int n = order[0]; + int n = z->order[0]; // non-interleaved data, we just need to process one block at a time, // in trivial scanline order // number of blocks to do just depends on how many actual "pixels" this // component has, independent of interleaved MCU blocking and such - int w = (img_comp[n].x+7) >> 3; - int h = (img_comp[n].y+7) >> 3; + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; for (j=0; j < h; ++j) { for (i=0; i < w; ++i) { - if (!decode_block(data, huff_dc+img_comp[n].hd, huff_ac+img_comp[n].ha, n)) return 0; - idct_block(img_comp[n].data+img_comp[n].w2*j*8+i*8, img_comp[n].w2, data, dequant[img_comp[n].tq]); + if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; + #if STBI_SIMD + stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); + #else + idct_block(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); + #endif // every data block is an MCU, so countdown the restart interval - if (--todo <= 0) { - if (code_bits < 24) grow_buffer_unsafe(); + if (--z->todo <= 0) { + if (z->code_bits < 24) grow_buffer_unsafe(z); // if it's NOT a restart, then just bail, so we get corrupt data // rather than no data - if (!RESTART(marker)) return 1; - reset(); + if (!RESTART(z->marker)) return 1; + reset(z); } } } } else { // interleaved! int i,j,k,x,y; short data[64]; - for (j=0; j < img_mcu_y; ++j) { - for (i=0; i < img_mcu_x; ++i) { + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { // scan an interleaved mcu... process scan_n components in order - for (k=0; k < scan_n; ++k) { - int n = order[k]; + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; // scan out an mcu's worth of this component; that's just determined // by the basic H and V specified for the component - for (y=0; y < img_comp[n].v; ++y) { - for (x=0; x < img_comp[n].h; ++x) { - int x2 = (i*img_comp[n].h + x)*8; - int y2 = (j*img_comp[n].v + y)*8; - if (!decode_block(data, huff_dc+img_comp[n].hd, huff_ac+img_comp[n].ha, n)) return 0; - idct_block(img_comp[n].data+img_comp[n].w2*y2+x2, img_comp[n].w2, data, dequant[img_comp[n].tq]); + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; + #if STBI_SIMD + stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); + #else + idct_block(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); + #endif } } } // after all interleaved components, that's an interleaved MCU, // so now count down the restart interval - if (--todo <= 0) { - if (code_bits < 24) grow_buffer_unsafe(); + if (--z->todo <= 0) { + if (z->code_bits < 24) grow_buffer_unsafe(z); // if it's NOT a restart, then just bail, so we get corrupt data // rather than no data - if (!RESTART(marker)) return 1; - reset(); + if (!RESTART(z->marker)) return 1; + reset(z); } } } @@ -1215,7 +1361,7 @@ static int parse_entropy_coded_data(void) return 1; } -static int process_marker(int m) +static int process_marker(jpeg *z, int m) { int L; switch (m) { @@ -1226,141 +1372,156 @@ static int process_marker(int m) return e("progressive jpeg","JPEG format not supported (progressive)"); case 0xDD: // DRI - specify restart interval - if (get16() != 4) return e("bad DRI len","Corrupt JPEG"); - restart_interval = get16(); + if (get16(&z->s) != 4) return e("bad DRI len","Corrupt JPEG"); + z->restart_interval = get16(&z->s); return 1; case 0xDB: // DQT - define quantization table - L = get16()-2; + L = get16(&z->s)-2; while (L > 0) { - int z = get8(); - int p = z >> 4; - int t = z & 15,i; + int q = get8(&z->s); + int p = q >> 4; + int t = q & 15,i; if (p != 0) return e("bad DQT type","Corrupt JPEG"); if (t > 3) return e("bad DQT table","Corrupt JPEG"); for (i=0; i < 64; ++i) - dequant[t][dezigzag[i]] = get8u(); + z->dequant[t][dezigzag[i]] = get8u(&z->s); + #if STBI_SIMD + for (i=0; i < 64; ++i) + z->dequant2[t][i] = dequant[t][i]; + #endif L -= 65; } return L==0; case 0xC4: // DHT - define huffman table - L = get16()-2; + L = get16(&z->s)-2; while (L > 0) { uint8 *v; int sizes[16],i,m=0; - int z = get8(); - int tc = z >> 4; - int th = z & 15; + int q = get8(&z->s); + int tc = q >> 4; + int th = q & 15; if (tc > 1 || th > 3) return e("bad DHT header","Corrupt JPEG"); for (i=0; i < 16; ++i) { - sizes[i] = get8(); + sizes[i] = get8(&z->s); m += sizes[i]; } L -= 17; if (tc == 0) { - if (!build_huffman(huff_dc+th, sizes)) return 0; - v = huff_dc[th].values; + if (!build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; } else { - if (!build_huffman(huff_ac+th, sizes)) return 0; - v = huff_ac[th].values; + if (!build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; } for (i=0; i < m; ++i) - v[i] = get8u(); + v[i] = get8u(&z->s); L -= m; } return L==0; } // check for comment block or APP blocks if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - skip(get16()-2); + skip(&z->s, get16(&z->s)-2); return 1; } return 0; } // after we see SOS -static int process_scan_header(void) +static int process_scan_header(jpeg *z) { int i; - int Ls = get16(); - scan_n = get8(); - if (scan_n < 1 || scan_n > 4 || scan_n > (int) img_n) return e("bad SOS component count","Corrupt JPEG"); - if (Ls != 6+2*scan_n) return e("bad SOS len","Corrupt JPEG"); - for (i=0; i < scan_n; ++i) { - int id = get8(), which; - int z = get8(); - for (which = 0; which < img_n; ++which) - if (img_comp[which].id == id) + int Ls = get16(&z->s); + z->scan_n = get8(&z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s.img_n) return e("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return e("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = get8(&z->s), which; + int q = get8(&z->s); + for (which = 0; which < z->s.img_n; ++which) + if (z->img_comp[which].id == id) break; - if (which == img_n) return 0; - img_comp[which].hd = z >> 4; if (img_comp[which].hd > 3) return e("bad DC huff","Corrupt JPEG"); - img_comp[which].ha = z & 15; if (img_comp[which].ha > 3) return e("bad AC huff","Corrupt JPEG"); - order[i] = which; + if (which == z->s.img_n) return 0; + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return e("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return e("bad AC huff","Corrupt JPEG"); + z->order[i] = which; } - if (get8() != 0) return e("bad SOS","Corrupt JPEG"); - get8(); // should be 63, but might be 0 - if (get8() != 0) return e("bad SOS","Corrupt JPEG"); + if (get8(&z->s) != 0) return e("bad SOS","Corrupt JPEG"); + get8(&z->s); // should be 63, but might be 0 + if (get8(&z->s) != 0) return e("bad SOS","Corrupt JPEG"); return 1; } -static int process_frame_header(int scan) +static int process_frame_header(jpeg *z, int scan) { - int Lf,p,i,z, h_max=1,v_max=1; - Lf = get16(); if (Lf < 11) return e("bad SOF len","Corrupt JPEG"); // JPEG - p = get8(); if (p != 8) return e("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline - img_y = get16(); if (img_y == 0) return e("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG - img_x = get16(); if (img_x == 0) return e("0 width","Corrupt JPEG"); // JPEG requires - img_n = get8(); - if (img_n != 3 && img_n != 1) return e("bad component count","Corrupt JPEG"); // JFIF requires + stbi *s = &z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = get16(s); if (Lf < 11) return e("bad SOF len","Corrupt JPEG"); // JPEG + p = get8(s); if (p != 8) return e("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = get16(s); if (s->img_y == 0) return e("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = get16(s); if (s->img_x == 0) return e("0 width","Corrupt JPEG"); // JPEG requires + c = get8(s); + if (c != 3 && c != 1) return e("bad component count","Corrupt JPEG"); // JFIF requires + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } - if (Lf != 8+3*img_n) return e("bad SOF len","Corrupt JPEG"); + if (Lf != 8+3*s->img_n) return e("bad SOF len","Corrupt JPEG"); - for (i=0; i < img_n; ++i) { - img_comp[i].id = get8(); - if (img_comp[i].id != i+1) // JFIF requires - if (img_comp[i].id != i) // jpegtran outputs non-JFIF-compliant files! + for (i=0; i < s->img_n; ++i) { + z->img_comp[i].id = get8(s); + if (z->img_comp[i].id != i+1) // JFIF requires + if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! return e("bad component ID","Corrupt JPEG"); - z = get8(); - img_comp[i].h = (z >> 4); if (!img_comp[i].h || img_comp[i].h > 4) return e("bad H","Corrupt JPEG"); - img_comp[i].v = z & 15; if (!img_comp[i].v || img_comp[i].v > 4) return e("bad V","Corrupt JPEG"); - img_comp[i].tq = get8(); if (img_comp[i].tq > 3) return e("bad TQ","Corrupt JPEG"); + q = get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return e("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return e("bad V","Corrupt JPEG"); + z->img_comp[i].tq = get8(s); if (z->img_comp[i].tq > 3) return e("bad TQ","Corrupt JPEG"); } if (scan != SCAN_load) return 1; - if ((1 << 30) / img_x / img_n < img_y) return e("too large", "Image too large to decode"); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); - for (i=0; i < img_n; ++i) { - if (img_comp[i].h > h_max) h_max = img_comp[i].h; - if (img_comp[i].v > v_max) v_max = img_comp[i].v; + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; } // compute interleaved mcu info - img_h_max = h_max; - img_v_max = v_max; - img_mcu_w = h_max * 8; - img_mcu_h = v_max * 8; - img_mcu_x = (img_x + img_mcu_w-1) / img_mcu_w; - img_mcu_y = (img_y + img_mcu_h-1) / img_mcu_h; + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; - for (i=0; i < img_n; ++i) { + for (i=0; i < s->img_n; ++i) { // number of effective pixels (e.g. for non-interleaved MCU) - img_comp[i].x = (img_x * img_comp[i].h + h_max-1) / h_max; - img_comp[i].y = (img_y * img_comp[i].v + v_max-1) / v_max; + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; // to simplify generation, we'll allocate enough memory to decode // the bogus oversized data from using interleaved MCUs and their // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't // discard the extra data until colorspace conversion - img_comp[i].w2 = img_mcu_x * img_comp[i].h * 8; - img_comp[i].h2 = img_mcu_y * img_comp[i].v * 8; - img_comp[i].data = (uint8 *) malloc(img_comp[i].w2 * img_comp[i].h2); - if (img_comp[i].data == NULL) { - for(--i; i >= 0; --i) - free(img_comp[i].data); + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].raw_data = malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); + if (z->img_comp[i].raw_data == NULL) { + for(--i; i >= 0; --i) { + free(z->img_comp[i].raw_data); + z->img_comp[i].data = NULL; + } return e("outofmem", "Out of memory"); } + // align blocks for installable-idct using mmx/sse + z->img_comp[i].data = (uint8*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + z->img_comp[i].linebuf = NULL; } return 1; @@ -1373,136 +1534,120 @@ static int process_frame_header(int scan) #define SOF(x) ((x) == 0xc0 || (x) == 0xc1) #define SOS(x) ((x) == 0xda) -static int decode_jpeg_header(int scan) +static int decode_jpeg_header(jpeg *z, int scan) { int m; - marker = MARKER_none; // initialize cached marker to empty - m = get_marker(); + z->marker = MARKER_none; // initialize cached marker to empty + m = get_marker(z); if (!SOI(m)) return e("no SOI","Corrupt JPEG"); if (scan == SCAN_type) return 1; - m = get_marker(); + m = get_marker(z); while (!SOF(m)) { - if (!process_marker(m)) return 0; - m = get_marker(); + if (!process_marker(z,m)) return 0; + m = get_marker(z); while (m == MARKER_none) { // some files have extra padding after their blocks, so ok, we'll scan - if (at_eof()) return e("no SOF", "Corrupt JPEG"); - m = get_marker(); + if (at_eof(&z->s)) return e("no SOF", "Corrupt JPEG"); + m = get_marker(z); } } - if (!process_frame_header(scan)) return 0; + if (!process_frame_header(z, scan)) return 0; return 1; } -static int decode_jpeg_image(void) +static int decode_jpeg_image(jpeg *j) { int m; - restart_interval = 0; - if (!decode_jpeg_header(SCAN_load)) return 0; - m = get_marker(); + j->restart_interval = 0; + if (!decode_jpeg_header(j, SCAN_load)) return 0; + m = get_marker(j); while (!EOI(m)) { if (SOS(m)) { - if (!process_scan_header()) return 0; - if (!parse_entropy_coded_data()) return 0; + if (!process_scan_header(j)) return 0; + if (!parse_entropy_coded_data(j)) return 0; } else { - if (!process_marker(m)) return 0; + if (!process_marker(j, m)) return 0; } - m = get_marker(); + m = get_marker(j); } return 1; } -// static jfif-centered resampling with cross-block smoothing -// here by cross-block smoothing what I mean is that the resampling -// is bilerp and crosses blocks; I dunno what IJG means +// static jfif-centered resampling (across block boundaries) + +typedef uint8 *(*resample_row_func)(uint8 *out, uint8 *in0, uint8 *in1, + int w, int hs); #define div4(x) ((uint8) ((x) >> 2)) -static void resample_v_2(uint8 *out1, uint8 *input, int w, int h, int s) +static uint8 *resample_row_1(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + return in_near; +} + +static uint8* resample_row_v_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) { // need to generate two samples vertically for every one in input - uint8 *above; - uint8 *below; - uint8 *source; - uint8 *out2; - int i,j; - source = input; - out2 = out1+w; - for (j=0; j < h; ++j) { - above = source; - source = input + j*s; - below = source + s; if (j == h-1) below = source; - for (i=0; i < w; ++i) { - int n = source[i]*3; - out1[i] = div4(above[i] + n); - out2[i] = div4(below[i] + n); - } - out1 += w*2; - out2 += w*2; - } + int i; + for (i=0; i < w; ++i) + out[i] = div4(3*in_near[i] + in_far[i] + 2); + return out; } -static void resample_h_2(uint8 *out, uint8 *input, int w, int h, int s) +static uint8* resample_row_h_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) { // need to generate two samples horizontally for every one in input - int i,j; + int i; + uint8 *input = in_near; if (w == 1) { - for (j=0; j < h; ++j) - out[j*2+0] = out[j*2+1] = input[j*s]; - return; + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; } - for (j=0; j < h; ++j) { - out[0] = input[0]; - out[1] = div4(input[0]*3 + input[1]); - for (i=1; i < w-1; ++i) { - int n = input[i]*3; - out[i*2-2] = div4(input[i-1] + n); - out[i*2-1] = div4(input[i+1] + n); - } - out[w*2-2] = div4(input[w-2]*3 + input[w-1]); - out[w*2-1] = input[w-1]; - out += w*2; - input += s; + + out[0] = input[0]; + out[1] = div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = div4(n+input[i-1]); + out[i*2+1] = div4(n+input[i+1]); } + out[i*2+0] = div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + return out; } -// .172 seconds on 3*anemones.jpg -static void resample_hv_2(uint8 *out, uint8 *input, int w, int h, int s) +#define div16(x) ((uint8) ((x) >> 4)) + +static uint8 *resample_row_hv_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) { // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = div16(3*t0 + t1 + 8); + out[i*2 ] = div16(3*t1 + t0 + 8); + } + out[w*2-1] = div4(t1+2); + return out; +} + +static uint8 *resample_row_generic(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // resample with nearest-neighbor int i,j; - int os = w*2; - // generate edge samples... @TODO lerp them! - for (i=0; i < w; ++i) { - out[i*2+0] = out[i*2+1] = input[i]; - out[i*2+(2*h-1)*os+0] = out[i*2+(2*h-1)*os+1] = input[i+(h-1)*w]; - } - for (j=0; j < h; ++j) { - out[j*os*2+0] = out[j*os*2+os+0] = input[j*w]; - out[j*os*2+os-1] = out[j*os*2+os+os-1] = input[j*w+i-1]; - } - // now generate interior samples; i & j point to top left of input - for (j=0; j < h-1; ++j) { - uint8 *in1 = input+j*s; - uint8 *in2 = in1 + s; - uint8 *out1 = out + (j*2+1)*os + 1; - uint8 *out2 = out1 + os; - for (i=0; i < w-1; ++i) { - int p00 = in1[0], p01=in1[1], p10=in2[0], p11=in2[1]; - int p00_3 = p00*3, p01_3 = p01*3, p10_3 = p10*3, p11_3 = p11*3; - - #define div16(x) ((uint8) ((x) >> 4)) - - out1[0] = div16(p00*9 + p01_3 + p10_3 + p11); - out1[1] = div16(p01*9 + p00_3 + p01_3 + p10); - out2[0] = div16(p10*9 + p11_3 + p00_3 + p01); - out2[1] = div16(p11*9 + p10_3 + p01_3 + p00); - out1 += 2; - out2 += 2; - ++in1; - ++in2; - } - } + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; } #define float2fixed(x) ((int) ((x) * 65536 + 0.5)) @@ -1529,109 +1674,143 @@ static void YCbCr_to_RGB_row(uint8 *out, uint8 *y, uint8 *pcb, uint8 *pcr, int c out[0] = (uint8)r; out[1] = (uint8)g; out[2] = (uint8)b; - if (step == 4) out[3] = 255; + out[3] = 255; out += step; } } +#if STBI_SIMD +static stbi_YCbCr_to_RGB_run stbi_YCbCr_installed = YCbCr_to_RGB_row; + +void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func) +{ + stbi_YCbCr_installed = func; +} +#endif + + // clean up the temporary component buffers -static void cleanup_jpeg(void) +static void cleanup_jpeg(jpeg *j) { int i; - for (i=0; i < img_n; ++i) { - if (img_comp[i].data) { - free(img_comp[i].data); - img_comp[i].data = NULL; + for (i=0; i < j->s.img_n; ++i) { + if (j->img_comp[i].data) { + free(j->img_comp[i].raw_data); + j->img_comp[i].data = NULL; + } + if (j->img_comp[i].linebuf) { + free(j->img_comp[i].linebuf); + j->img_comp[i].linebuf = NULL; } } } -static uint8 *load_jpeg_image(int *out_x, int *out_y, int *comp, int req_comp) +typedef struct { - int i, n; + resample_row_func resample; + uint8 *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi_resample; + +static uint8 *load_jpeg_image(jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n; // validate req_comp - if (req_comp < 0 || req_comp > 4) return ep("bad req_comp", "Internal error"); + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + z->s.img_n = 0; // load a jpeg image from whichever source - if (!decode_jpeg_image()) { cleanup_jpeg(); return NULL; } + if (!decode_jpeg_image(z)) { cleanup_jpeg(z); return NULL; } // determine actual number of components to generate - n = req_comp ? req_comp : img_n; + n = req_comp ? req_comp : z->s.img_n; - // resample components to full size... memory wasteful, but this - // lets us bilerp across blocks while upsampling - for (i=0; i < img_n; ++i) { - // if we're outputting fewer than 3 components, we're grey not RGB; - // in that case, don't bother upsampling Cb or Cr - if (n < 3 && i) continue; + if (z->s.img_n == 3 && n < 3) + decode_n = 1; + else + decode_n = z->s.img_n; - // check if the component scale is less than max; if so it needs upsampling - if (img_comp[i].h != img_h_max || img_comp[i].v != img_v_max) { - int stride = img_x; - // allocate final size; make sure it's big enough for upsampling off - // the edges with upsample up to 4x4 (although we only support 2x2 - // currently) - uint8 *new_data = (uint8 *) malloc((img_x+3)*(img_y+3)); - if (new_data == NULL) { - cleanup_jpeg(); - return ep("outofmem", "Out of memory (image too large?)"); - } - if (img_comp[i].h*2 == img_h_max && img_comp[i].v*2 == img_v_max) { - int tx = (img_x+1)>>1; - resample_hv_2(new_data, img_comp[i].data, tx,(img_y+1)>>1, img_comp[i].w2); - stride = tx*2; - } else if (img_comp[i].h == img_h_max && img_comp[i].v*2 == img_v_max) { - resample_v_2(new_data, img_comp[i].data, img_x,(img_y+1)>>1, img_comp[i].w2); - } else if (img_comp[i].h*2 == img_h_max && img_comp[i].v == img_v_max) { - int tx = (img_x+1)>>1; - resample_h_2(new_data, img_comp[i].data, tx,img_y, img_comp[i].w2); - stride = tx*2; - } else { - // @TODO resample uncommon sampling pattern with nearest neighbor - free(new_data); - cleanup_jpeg(); - return ep("uncommon H or V", "JPEG not supported: atypical downsampling mode"); - } - img_comp[i].w2 = stride; - free(img_comp[i].data); - img_comp[i].data = new_data; - } - } - - // now convert components to output image + // resample and color-convert { - uint32 i,j; - uint8 *output = (uint8 *) malloc(n * img_x * img_y + 1); - if (n >= 3) { // output STBI_rgb_* - for (j=0; j < img_y; ++j) { - uint8 *y = img_comp[0].data + j*img_comp[0].w2; - uint8 *out = output + n * img_x * j; - if (img_n == 3) { - uint8 *cb = img_comp[1].data + j*img_comp[1].w2; - uint8 *cr = img_comp[2].data + j*img_comp[2].w2; - YCbCr_to_RGB_row(out, y, cb, cr, img_x, n); - } else { - for (i=0; i < img_x; ++i) { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n == 3 - out += n; - } + int k; + uint i,j; + uint8 *output; + uint8 *coutput[4]; + + stbi_resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (uint8 *) malloc(z->s.img_x + 3); + if (!z->img_comp[k].linebuf) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s.img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = resample_row_hv_2; + else r->resample = resample_row_generic; + } + + // can't error after this so, this is safe + output = (uint8 *) malloc(n * z->s.img_x * z->s.img_y + 1); + if (!output) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s.img_y; ++j) { + uint8 *out = output + n * z->s.img_x * j; + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; } } - } else { // output STBI_grey_* - for (j=0; j < img_y; ++j) { - uint8 *y = img_comp[0].data + j*img_comp[0].w2; - uint8 *out = output + n * img_x * j; + if (n >= 3) { + uint8 *y = coutput[0]; + if (z->s.img_n == 3) { + #if STBI_SIMD + stbi_YCbCr_installed(out, y, coutput[1], coutput[2], z->s.img_x, n); + #else + YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], z->s.img_x, n); + #endif + } else + for (i=0; i < z->s.img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + uint8 *y = coutput[0]; if (n == 1) - for (i=0; i < img_x; ++i) *out++ = *y++; + for (i=0; i < z->s.img_x; ++i) out[i] = y[i]; else - for (i=0; i < img_x; ++i) *out++ = *y++, *out++ = 255; + for (i=0; i < z->s.img_x; ++i) *out++ = y[i], *out++ = 255; } } - cleanup_jpeg(); - *out_x = img_x; - *out_y = img_y; - if (comp) *comp = img_n; // report original components, not output + cleanup_jpeg(z); + *out_x = z->s.img_x; + *out_y = z->s.img_y; + if (comp) *comp = z->s.img_n; // report original components, not output return output; } } @@ -1639,11 +1818,12 @@ static uint8 *load_jpeg_image(int *out_x, int *out_y, int *comp, int req_comp) #ifndef STBI_NO_STDIO unsigned char *stbi_jpeg_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) { - start_file(f); - return load_jpeg_image(x,y,comp,req_comp); + jpeg j; + start_file(&j.s, f); + return load_jpeg_image(&j, x,y,comp,req_comp); } -unsigned char *stbi_jpeg_load(char *filename, int *x, int *y, int *comp, int req_comp) +unsigned char *stbi_jpeg_load(char const *filename, int *x, int *y, int *comp, int req_comp) { unsigned char *data; FILE *f = fopen(filename, "rb"); @@ -1654,34 +1834,39 @@ unsigned char *stbi_jpeg_load(char *filename, int *x, int *y, int *comp, int req } #endif -unsigned char *stbi_jpeg_load_from_memory(stbi_uc *buffer, int len, int *x, int *y, int *comp, int req_comp) +unsigned char *stbi_jpeg_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { - start_mem(buffer,len); - return load_jpeg_image(x,y,comp,req_comp); + jpeg j; + start_mem(&j.s, buffer,len); + return load_jpeg_image(&j, x,y,comp,req_comp); } #ifndef STBI_NO_STDIO int stbi_jpeg_test_file(FILE *f) { int n,r; + jpeg j; n = ftell(f); - start_file(f); - r = decode_jpeg_header(SCAN_type); + start_file(&j.s, f); + r = decode_jpeg_header(&j, SCAN_type); fseek(f,n,SEEK_SET); return r; } #endif -int stbi_jpeg_test_memory(unsigned char *buffer, int len) +int stbi_jpeg_test_memory(stbi_uc const *buffer, int len) { - start_mem(buffer,len); - return decode_jpeg_header(SCAN_type); + jpeg j; + start_mem(&j.s, buffer,len); + return decode_jpeg_header(&j, SCAN_type); } // @TODO: -extern int stbi_jpeg_info (char *filename, int *x, int *y, int *comp); +#ifndef STBI_NO_STDIO +extern int stbi_jpeg_info (char const *filename, int *x, int *y, int *comp); extern int stbi_jpeg_info_from_file (FILE *f, int *x, int *y, int *comp); -extern int stbi_jpeg_info_from_memory(stbi_uc *buffer, int len, int *x, int *y, int *comp); +#endif +extern int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); // public domain zlib decode v0.2 Sean Barrett 2006-11-18 // simple implementation @@ -1774,51 +1959,60 @@ static int zbuild_huffman(zhuffman *z, uint8 *sizelist, int num) // we require PNG read all the IDATs and combine them into a single // memory buffer -static uint8 *zbuffer, *zbuffer_end; - -__forceinline static int zget8(void) +typedef struct { - if (zbuffer >= zbuffer_end) return 0; - return *zbuffer++; + uint8 *zbuffer, *zbuffer_end; + int num_bits; + uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + zhuffman z_length, z_distance; +} zbuf; + +__forceinline static int zget8(zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; } -//static unsigned long code_buffer; -static int num_bits; - -static void fill_bits(void) +static void fill_bits(zbuf *z) { do { - assert(code_buffer < (1U << num_bits)); - code_buffer |= zget8() << num_bits; - num_bits += 8; - } while (num_bits <= 24); + assert(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); } -__forceinline static unsigned int zreceive(int n) +__forceinline static unsigned int zreceive(zbuf *z, int n) { unsigned int k; - if (num_bits < n) fill_bits(); - k = code_buffer & ((1 << n) - 1); - code_buffer >>= n; - num_bits -= n; + if (z->num_bits < n) fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; return k; } -__forceinline static int zhuffman_decode(zhuffman *z) +__forceinline static int zhuffman_decode(zbuf *a, zhuffman *z) { int b,s,k; - if (num_bits < 16) fill_bits(); - b = z->fast[code_buffer & ZFAST_MASK]; + if (a->num_bits < 16) fill_bits(a); + b = z->fast[a->code_buffer & ZFAST_MASK]; if (b < 0xffff) { s = z->size[b]; - code_buffer >>= s; - num_bits -= s; + a->code_buffer >>= s; + a->num_bits -= s; return z->value[b]; } // not resolved by fast table, so compute it the slow way // use jpeg approach, which requires MSbits at top - k = bit_reverse(code_buffer, 16); + k = bit_reverse(a->code_buffer, 16); for (s=ZFAST_BITS+1; ; ++s) if (k < z->maxcode[s]) break; @@ -1826,35 +2020,28 @@ __forceinline static int zhuffman_decode(zhuffman *z) // code size is s, so: b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; assert(z->size[b] == s); - code_buffer >>= s; - num_bits -= s; + a->code_buffer >>= s; + a->num_bits -= s; return z->value[b]; } -static char *zout; -static char *zout_start; -static char *zout_end; -static int z_expandable; - -static int expand(int n) // need to make room for n bytes +static int expand(zbuf *z, int n) // need to make room for n bytes { char *q; int cur, limit; - if (!z_expandable) return e("output buffer limit","Corrupt PNG"); - cur = (int) (zout - zout_start); - limit = (int) (zout_end - zout_start); + if (!z->z_expandable) return e("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = (int) (z->zout_end - z->zout_start); while (cur + n > limit) limit *= 2; - q = (char *) realloc(zout_start, limit); + q = (char *) realloc(z->zout_start, limit); if (q == NULL) return e("outofmem", "Out of memory"); - zout_start = q; - zout = q + cur; - zout_end = q + limit; + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; return 1; } -static zhuffman z_length, z_distance; - static int length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, @@ -1869,35 +2056,35 @@ static int dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, static int dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; -static int parse_huffman_block(void) +static int parse_huffman_block(zbuf *a) { for(;;) { - int z = zhuffman_decode(&z_length); + int z = zhuffman_decode(a, &a->z_length); if (z < 256) { if (z < 0) return e("bad huffman code","Corrupt PNG"); // error in huffman codes - if (zout >= zout_end) if (!expand(1)) return 0; - *zout++ = (char) z; + if (a->zout >= a->zout_end) if (!expand(a, 1)) return 0; + *a->zout++ = (char) z; } else { uint8 *p; int len,dist; if (z == 256) return 1; z -= 257; len = length_base[z]; - if (length_extra[z]) len += zreceive(length_extra[z]); - z = zhuffman_decode(&z_distance); + if (length_extra[z]) len += zreceive(a, length_extra[z]); + z = zhuffman_decode(a, &a->z_distance); if (z < 0) return e("bad huffman code","Corrupt PNG"); dist = dist_base[z]; - if (dist_extra[z]) dist += zreceive(dist_extra[z]); - if (zout - zout_start < dist) return e("bad dist","Corrupt PNG"); - if (zout + len > zout_end) if (!expand(len)) return 0; - p = (uint8 *) (zout - dist); + if (dist_extra[z]) dist += zreceive(a, dist_extra[z]); + if (a->zout - a->zout_start < dist) return e("bad dist","Corrupt PNG"); + if (a->zout + len > a->zout_end) if (!expand(a, len)) return 0; + p = (uint8 *) (a->zout - dist); while (len--) - *zout++ = *p++; + *a->zout++ = *p++; } } } -static int compute_huffman_codes(void) +static int compute_huffman_codes(zbuf *a) { static uint8 length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; static zhuffman z_codelength; // static just to save stack space @@ -1905,79 +2092,79 @@ static int compute_huffman_codes(void) uint8 codelength_sizes[19]; int i,n; - int hlit = zreceive(5) + 257; - int hdist = zreceive(5) + 1; - int hclen = zreceive(4) + 4; + int hlit = zreceive(a,5) + 257; + int hdist = zreceive(a,5) + 1; + int hclen = zreceive(a,4) + 4; memset(codelength_sizes, 0, sizeof(codelength_sizes)); for (i=0; i < hclen; ++i) { - int s = zreceive(3); + int s = zreceive(a,3); codelength_sizes[length_dezigzag[i]] = (uint8) s; } if (!zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; n = 0; while (n < hlit + hdist) { - int c = zhuffman_decode(&z_codelength); + int c = zhuffman_decode(a, &z_codelength); assert(c >= 0 && c < 19); if (c < 16) lencodes[n++] = (uint8) c; else if (c == 16) { - c = zreceive(2)+3; + c = zreceive(a,2)+3; memset(lencodes+n, lencodes[n-1], c); n += c; } else if (c == 17) { - c = zreceive(3)+3; + c = zreceive(a,3)+3; memset(lencodes+n, 0, c); n += c; } else { assert(c == 18); - c = zreceive(7)+11; + c = zreceive(a,7)+11; memset(lencodes+n, 0, c); n += c; } } if (n != hlit+hdist) return e("bad codelengths","Corrupt PNG"); - if (!zbuild_huffman(&z_length, lencodes, hlit)) return 0; - if (!zbuild_huffman(&z_distance, lencodes+hlit, hdist)) return 0; + if (!zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; return 1; } -static int parse_uncompressed_block(void) +static int parse_uncompressed_block(zbuf *a) { uint8 header[4]; int len,nlen,k; - if (num_bits & 7) - zreceive(num_bits & 7); // discard + if (a->num_bits & 7) + zreceive(a, a->num_bits & 7); // discard // drain the bit-packed data into header k = 0; - while (num_bits > 0) { - header[k++] = (uint8) (code_buffer & 255); // wtf this warns? - code_buffer >>= 8; - num_bits -= 8; + while (a->num_bits > 0) { + header[k++] = (uint8) (a->code_buffer & 255); // wtf this warns? + a->code_buffer >>= 8; + a->num_bits -= 8; } - assert(num_bits == 0); + assert(a->num_bits == 0); // now fill header the normal way while (k < 4) - header[k++] = (uint8) zget8(); + header[k++] = (uint8) zget8(a); len = header[1] * 256 + header[0]; nlen = header[3] * 256 + header[2]; if (nlen != (len ^ 0xffff)) return e("zlib corrupt","Corrupt PNG"); - if (zbuffer + len > zbuffer_end) return e("read past buffer","Corrupt PNG"); - if (zout + len > zout_end) - if (!expand(len)) return 0; - memcpy(zout, zbuffer, len); - zbuffer += len; - zout += len; + if (a->zbuffer + len > a->zbuffer_end) return e("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!expand(a, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; return 1; } -static int parse_zlib_header(void) +static int parse_zlib_header(zbuf *a) { - int cmf = zget8(); + int cmf = zget8(a); int cm = cmf & 15; /* int cinfo = cmf >> 4; */ - int flg = zget8(); + int flg = zget8(a); if ((cmf*256+flg) % 31 != 0) return e("bad zlib header","Corrupt PNG"); // zlib spec if (flg & 32) return e("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png if (cm != 8) return e("bad compression","Corrupt PNG"); // DEFLATE required for png @@ -1985,6 +2172,7 @@ static int parse_zlib_header(void) return 1; } +// @TODO: should statically initialize these for optimal thread safety static uint8 default_length[288], default_distance[32]; static void init_defaults(void) { @@ -1997,96 +2185,100 @@ static void init_defaults(void) for (i=0; i <= 31; ++i) default_distance[i] = 5; } -static int parse_zlib(int parse_header) +static int parse_zlib(zbuf *a, int parse_header) { int final, type; if (parse_header) - if (!parse_zlib_header()) return 0; - num_bits = 0; - code_buffer = 0; + if (!parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; do { - final = zreceive(1); - type = zreceive(2); + final = zreceive(a,1); + type = zreceive(a,2); if (type == 0) { - if (!parse_uncompressed_block()) return 0; + if (!parse_uncompressed_block(a)) return 0; } else if (type == 3) { return 0; } else { if (type == 1) { // use fixed code lengths - if (!default_length[0]) init_defaults(); - if (!zbuild_huffman(&z_length , default_length , 288)) return 0; - if (!zbuild_huffman(&z_distance, default_distance, 32)) return 0; + if (!default_distance[31]) init_defaults(); + if (!zbuild_huffman(&a->z_length , default_length , 288)) return 0; + if (!zbuild_huffman(&a->z_distance, default_distance, 32)) return 0; } else { - if (!compute_huffman_codes()) return 0; + if (!compute_huffman_codes(a)) return 0; } - if (!parse_huffman_block()) return 0; + if (!parse_huffman_block(a)) return 0; } } while (!final); return 1; } -static int do_zlib(char *obuf, int olen, int exp, int parse_header) +static int do_zlib(zbuf *a, char *obuf, int olen, int exp, int parse_header) { - zout_start = obuf; - zout = obuf; - zout_end = obuf + olen; - z_expandable = exp; + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; - return parse_zlib(parse_header); + return parse_zlib(a, parse_header); } -char *stbi_zlib_decode_malloc_guesssize(int initial_size, int *outlen) +char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) { + zbuf a; char *p = (char *) malloc(initial_size); if (p == NULL) return NULL; - if (do_zlib(p, initial_size, 1, 1)) { - *outlen = (int) (zout - zout_start); - return zout_start; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer + len; + if (do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; } else { - free(zout_start); + free(a.zout_start); return NULL; } } -char *stbi_zlib_decode_malloc(char *buffer, int len, int *outlen) +char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) { - zbuffer = (uint8 *) buffer; - zbuffer_end = (uint8 *) buffer+len; - return stbi_zlib_decode_malloc_guesssize(16384, outlen); + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); } -int stbi_zlib_decode_buffer(char *obuffer, int olen, char *ibuffer, int ilen) +int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) { - zbuffer = (uint8 *) ibuffer; - zbuffer_end = (uint8 *) ibuffer + ilen; - if (do_zlib(obuffer, olen, 0, 1)) - return (int) (zout - zout_start); + zbuf a; + a.zbuffer = (uint8 *) ibuffer; + a.zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); else return -1; } -char *stbi_zlib_decode_noheader_malloc(char *buffer, int len, int *outlen) +char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) { + zbuf a; char *p = (char *) malloc(16384); if (p == NULL) return NULL; - zbuffer = (uint8 *) buffer; - zbuffer_end = (uint8 *) buffer+len; - if (do_zlib(p, 16384, 1, 0)) { - *outlen = (int) (zout - zout_start); - return zout_start; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer+len; + if (do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; } else { - free(zout_start); + free(a.zout_start); return NULL; } } -int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, char *ibuffer, int ilen) +int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) { - zbuffer = (uint8 *) ibuffer; - zbuffer_end = (uint8 *) ibuffer + ilen; - if (do_zlib(obuffer, olen, 0, 0)) - return (int) (zout - zout_start); + zbuf a; + a.zbuffer = (uint8 *) ibuffer; + a.zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); else return -1; } @@ -2104,30 +2296,35 @@ int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, char *ibuffer, int typedef struct { - unsigned long length; - unsigned long type; + uint32 length; + uint32 type; } chunk; #define PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) -static chunk get_chunk_header(void) +static chunk get_chunk_header(stbi *s) { chunk c; - c.length = get32(); - c.type = get32(); + c.length = get32(s); + c.type = get32(s); return c; } -static int check_png_header(void) +static int check_png_header(stbi *s) { static uint8 png_sig[8] = { 137,80,78,71,13,10,26,10 }; int i; for (i=0; i < 8; ++i) - if (get8() != png_sig[i]) return e("bad png sig","Not a PNG"); + if (get8(s) != png_sig[i]) return e("bad png sig","Not a PNG"); return 1; } -static uint8 *idata, *expanded, *out; +typedef struct +{ + stbi s; + uint8 *idata, *expanded, *out; +} png; + enum { F_none=0, F_sub=1, F_up=2, F_avg=3, F_paeth=4, @@ -2151,16 +2348,18 @@ static int paeth(int a, int b, int c) } // create the png data from post-deflated data -static int create_png_image(uint8 *raw, uint32 raw_len, int out_n) +static int create_png_image(png *a, uint8 *raw, uint32 raw_len, int out_n) { - uint32 i,j,stride = img_x*out_n; + stbi *s = &a->s; + uint32 i,j,stride = s->img_x*out_n; int k; - assert(out_n == img_n || out_n == img_n+1); - out = (uint8 *) malloc(img_x * img_y * out_n); - if (!out) return e("outofmem", "Out of memory"); - if (raw_len != (img_n * img_x + 1) * img_y) return e("not enough pixels","Corrupt PNG"); - for (j=0; j < img_y; ++j) { - uint8 *cur = out + stride*j; + int img_n = s->img_n; // copy it into a local for later + assert(out_n == s->img_n || out_n == s->img_n+1); + a->out = (uint8 *) malloc(s->img_x * s->img_y * out_n); + if (!a->out) return e("outofmem", "Out of memory"); + if (raw_len != (img_n * s->img_x + 1) * s->img_y) return e("not enough pixels","Corrupt PNG"); + for (j=0; j < s->img_y; ++j) { + uint8 *cur = a->out + stride*j; uint8 *prior = cur - stride; int filter = *raw++; if (filter > 4) return e("invalid filter","Corrupt PNG"); @@ -2186,7 +2385,7 @@ static int create_png_image(uint8 *raw, uint32 raw_len, int out_n) if (img_n == out_n) { #define CASE(f) \ case f: \ - for (i=1; i < img_x; ++i, raw+=img_n,cur+=img_n,prior+=img_n) \ + for (i=s->img_x-1; i >= 1; --i, raw+=img_n,cur+=img_n,prior+=img_n) \ for (k=0; k < img_n; ++k) switch(filter) { CASE(F_none) cur[k] = raw[k]; break; @@ -2202,7 +2401,7 @@ static int create_png_image(uint8 *raw, uint32 raw_len, int out_n) assert(img_n+1 == out_n); #define CASE(f) \ case f: \ - for (i=1; i < img_x; ++i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ + for (i=s->img_x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ for (k=0; k < img_n; ++k) switch(filter) { CASE(F_none) cur[k] = raw[k]; break; @@ -2219,16 +2418,16 @@ static int create_png_image(uint8 *raw, uint32 raw_len, int out_n) return 1; } -static int compute_transparency(uint8 tc[3], int out_n) +static int compute_transparency(png *z, uint8 tc[3], int out_n) { - uint32 i, pixel_count = img_x * img_y; - uint8 *p = out; + stbi *s = &z->s; + uint32 i, pixel_count = s->img_x * s->img_y; + uint8 *p = z->out; // compute color-based transparency, assuming we've // already got 255 as the alpha value in the output assert(out_n == 2 || out_n == 4); - p = out; if (out_n == 2) { for (i=0; i < pixel_count; ++i) { p[1] = (p[0] == tc[0] ? 0 : 255); @@ -2244,10 +2443,10 @@ static int compute_transparency(uint8 tc[3], int out_n) return 1; } -static int expand_palette(uint8 *palette, int len, int pal_img_n) +static int expand_palette(png *a, uint8 *palette, int len, int pal_img_n) { - uint32 i, pixel_count = img_x * img_y; - uint8 *p, *temp_out, *orig = out; + uint32 i, pixel_count = a->s.img_x * a->s.img_y; + uint8 *p, *temp_out, *orig = a->out; p = (uint8 *) malloc(pixel_count * pal_img_n); if (p == NULL) return e("outofmem", "Out of memory"); @@ -2273,24 +2472,25 @@ static int expand_palette(uint8 *palette, int len, int pal_img_n) p += 4; } } - free(out); - out = temp_out; + free(a->out); + a->out = temp_out; return 1; } -static int parse_png_file(int scan, int req_comp) +static int parse_png_file(png *z, int scan, int req_comp) { uint8 palette[1024], pal_img_n=0; uint8 has_trans=0, tc[3]; uint32 ioff=0, idata_limit=0, i, pal_len=0; int first=1,k; + stbi *s = &z->s; - if (!check_png_header()) return 0; + if (!check_png_header(s)) return 0; if (scan == SCAN_type) return 1; for(;;first=0) { - chunk c = get_chunk_header(); + chunk c = get_chunk_header(s); if (first && c.type != PNG_TYPE('I','H','D','R')) return e("first not IHDR","Corrupt PNG"); switch (c.type) { @@ -2298,24 +2498,24 @@ static int parse_png_file(int scan, int req_comp) int depth,color,interlace,comp,filter; if (!first) return e("multiple IHDR","Corrupt PNG"); if (c.length != 13) return e("bad IHDR len","Corrupt PNG"); - img_x = get32(); if (img_x > (1 << 24)) return e("too large","Very large image (corrupt?)"); - img_y = get32(); if (img_y > (1 << 24)) return e("too large","Very large image (corrupt?)"); - depth = get8(); if (depth != 8) return e("8bit only","PNG not supported: 8-bit only"); - color = get8(); if (color > 6) return e("bad ctype","Corrupt PNG"); + s->img_x = get32(s); if (s->img_x > (1 << 24)) return e("too large","Very large image (corrupt?)"); + s->img_y = get32(s); if (s->img_y > (1 << 24)) return e("too large","Very large image (corrupt?)"); + depth = get8(s); if (depth != 8) return e("8bit only","PNG not supported: 8-bit only"); + color = get8(s); if (color > 6) return e("bad ctype","Corrupt PNG"); if (color == 3) pal_img_n = 3; else if (color & 1) return e("bad ctype","Corrupt PNG"); - comp = get8(); if (comp) return e("bad comp method","Corrupt PNG"); - filter= get8(); if (filter) return e("bad filter method","Corrupt PNG"); - interlace = get8(); if (interlace) return e("interlaced","PNG not supported: interlaced mode"); - if (!img_x || !img_y) return e("0-pixel image","Corrupt PNG"); + comp = get8(s); if (comp) return e("bad comp method","Corrupt PNG"); + filter= get8(s); if (filter) return e("bad filter method","Corrupt PNG"); + interlace = get8(s); if (interlace) return e("interlaced","PNG not supported: interlaced mode"); + if (!s->img_x || !s->img_y) return e("0-pixel image","Corrupt PNG"); if (!pal_img_n) { - img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / img_x / img_n < img_y) return e("too large", "Image too large to decode"); + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); if (scan == SCAN_header) return 1; } else { // if paletted, then pal_n is our final components, and // img_n is # components to decompress/filter. - img_n = 1; - if ((1 << 30) / img_x / 4 < img_y) return e("too large","Corrupt PNG"); + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return e("too large","Corrupt PNG"); // if SCAN_header, have to scan to see if we have a tRNS } break; @@ -2326,54 +2526,54 @@ static int parse_png_file(int scan, int req_comp) pal_len = c.length / 3; if (pal_len * 3 != c.length) return e("invalid PLTE","Corrupt PNG"); for (i=0; i < pal_len; ++i) { - palette[i*4+0] = get8u(); - palette[i*4+1] = get8u(); - palette[i*4+2] = get8u(); + palette[i*4+0] = get8u(s); + palette[i*4+1] = get8u(s); + palette[i*4+2] = get8u(s); palette[i*4+3] = 255; } break; } case PNG_TYPE('t','R','N','S'): { - if (idata) return e("tRNS after IDAT","Corrupt PNG"); + if (z->idata) return e("tRNS after IDAT","Corrupt PNG"); if (pal_img_n) { - if (scan == SCAN_header) { img_n = 4; return 1; } + if (scan == SCAN_header) { s->img_n = 4; return 1; } if (pal_len == 0) return e("tRNS before PLTE","Corrupt PNG"); if (c.length > pal_len) return e("bad tRNS len","Corrupt PNG"); pal_img_n = 4; for (i=0; i < c.length; ++i) - palette[i*4+3] = get8u(); + palette[i*4+3] = get8u(s); } else { - if (!(img_n & 1)) return e("tRNS with alpha","Corrupt PNG"); - if (c.length != (uint32) img_n*2) return e("bad tRNS len","Corrupt PNG"); + if (!(s->img_n & 1)) return e("tRNS with alpha","Corrupt PNG"); + if (c.length != (uint32) s->img_n*2) return e("bad tRNS len","Corrupt PNG"); has_trans = 1; - for (k=0; k < img_n; ++k) - tc[k] = (uint8) get16(); // non 8-bit images will be larger + for (k=0; k < s->img_n; ++k) + tc[k] = (uint8) get16(s); // non 8-bit images will be larger } break; } case PNG_TYPE('I','D','A','T'): { if (pal_img_n && !pal_len) return e("no PLTE","Corrupt PNG"); - if (scan == SCAN_header) { img_n = pal_img_n; return 1; } + if (scan == SCAN_header) { s->img_n = pal_img_n; return 1; } if (ioff + c.length > idata_limit) { uint8 *p; if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; while (ioff + c.length > idata_limit) idata_limit *= 2; - p = (uint8 *) realloc(idata, idata_limit); if (p == NULL) return e("outofmem", "Out of memory"); - idata = p; + p = (uint8 *) realloc(z->idata, idata_limit); if (p == NULL) return e("outofmem", "Out of memory"); + z->idata = p; } #ifndef STBI_NO_STDIO - if (img_file) + if (s->img_file) { - if (fread(idata+ioff,1,c.length,img_file) != c.length) return e("outofdata","Corrupt PNG"); + if (fread(z->idata+ioff,1,c.length,s->img_file) != c.length) return e("outofdata","Corrupt PNG"); } else #endif { - memcpy(idata+ioff, img_buffer, c.length); - img_buffer += c.length; + memcpy(z->idata+ioff, s->img_buffer, c.length); + s->img_buffer += c.length; } ioff += c.length; break; @@ -2382,26 +2582,26 @@ static int parse_png_file(int scan, int req_comp) case PNG_TYPE('I','E','N','D'): { uint32 raw_len; if (scan != SCAN_load) return 1; - if (idata == NULL) return e("no IDAT","Corrupt PNG"); - expanded = (uint8 *) stbi_zlib_decode_malloc((char *) idata, ioff, (int *) &raw_len); - if (expanded == NULL) return 0; // zlib should set error - free(idata); idata = NULL; - if ((req_comp == img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) - img_out_n = img_n+1; + if (z->idata == NULL) return e("no IDAT","Corrupt PNG"); + z->expanded = (uint8 *) stbi_zlib_decode_malloc((char *) z->idata, ioff, (int *) &raw_len); + if (z->expanded == NULL) return 0; // zlib should set error + free(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; else - img_out_n = img_n; - if (!create_png_image(expanded, raw_len, img_out_n)) return 0; + s->img_out_n = s->img_n; + if (!create_png_image(z, z->expanded, raw_len, s->img_out_n)) return 0; if (has_trans) - if (!compute_transparency(tc, img_out_n)) return 0; + if (!compute_transparency(z, tc, s->img_out_n)) return 0; if (pal_img_n) { // pal_img_n == 3 or 4 - img_n = pal_img_n; // record the actual colors we had - img_out_n = pal_img_n; - if (req_comp >= 3) img_out_n = req_comp; - if (!expand_palette(palette, pal_len, img_out_n)) + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!expand_palette(z, palette, pal_len, s->img_out_n)) return 0; } - free(expanded); expanded = NULL; + free(z->expanded); z->expanded = NULL; return 1; } @@ -2409,6 +2609,7 @@ static int parse_png_file(int scan, int req_comp) // if critical, fail if ((c.type & (1 << 29)) == 0) { #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe static char invalid_chunk[] = "XXXX chunk not known"; invalid_chunk[0] = (uint8) (c.type >> 24); invalid_chunk[1] = (uint8) (c.type >> 16); @@ -2417,32 +2618,36 @@ static int parse_png_file(int scan, int req_comp) #endif return e(invalid_chunk, "PNG not supported: unknown chunk type"); } - skip(c.length); + skip(s, c.length); break; } // end of chunk, read and skip CRC - get8(); get8(); get8(); get8(); + get32(s); } } -static unsigned char *do_png(int *x, int *y, int *n, int req_comp) +static unsigned char *do_png(png *p, int *x, int *y, int *n, int req_comp) { unsigned char *result=NULL; - if (req_comp < 0 || req_comp > 4) return ep("bad req_comp", "Internal error"); - if (parse_png_file(SCAN_load, req_comp)) { - result = out; - out = NULL; - if (req_comp && req_comp != img_out_n) { - result = convert_format(result, img_out_n, req_comp); + p->expanded = NULL; + p->idata = NULL; + p->out = NULL; + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + if (parse_png_file(p, SCAN_load, req_comp)) { + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s.img_out_n) { + result = convert_format(result, p->s.img_out_n, req_comp, p->s.img_x, p->s.img_y); + p->s.img_out_n = req_comp; if (result == NULL) return result; } - *x = img_x; - *y = img_y; - if (n) *n = img_n; + *x = p->s.img_x; + *y = p->s.img_y; + if (n) *n = p->s.img_n; } - free(out); out = NULL; - free(expanded); expanded = NULL; - free(idata); idata = NULL; + free(p->out); p->out = NULL; + free(p->expanded); p->expanded = NULL; + free(p->idata); p->idata = NULL; return result; } @@ -2450,11 +2655,12 @@ static unsigned char *do_png(int *x, int *y, int *n, int req_comp) #ifndef STBI_NO_STDIO unsigned char *stbi_png_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) { - start_file(f); - return do_png(x,y,comp,req_comp); + png p; + start_file(&p.s, f); + return do_png(&p, x,y,comp,req_comp); } -unsigned char *stbi_png_load(char *filename, int *x, int *y, int *comp, int req_comp) +unsigned char *stbi_png_load(char const *filename, int *x, int *y, int *comp, int req_comp) { unsigned char *data; FILE *f = fopen(filename, "rb"); @@ -2465,47 +2671,52 @@ unsigned char *stbi_png_load(char *filename, int *x, int *y, int *comp, int req_ } #endif -unsigned char *stbi_png_load_from_memory(unsigned char *buffer, int len, int *x, int *y, int *comp, int req_comp) +unsigned char *stbi_png_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { - start_mem(buffer,len); - return do_png(x,y,comp,req_comp); + png p; + start_mem(&p.s, buffer,len); + return do_png(&p, x,y,comp,req_comp); } #ifndef STBI_NO_STDIO int stbi_png_test_file(FILE *f) { + png p; int n,r; n = ftell(f); - start_file(f); - r = parse_png_file(SCAN_type,STBI_default); + start_file(&p.s, f); + r = parse_png_file(&p, SCAN_type,STBI_default); fseek(f,n,SEEK_SET); return r; } #endif -int stbi_png_test_memory(unsigned char *buffer, int len) +int stbi_png_test_memory(stbi_uc const *buffer, int len) { - start_mem(buffer, len); - return parse_png_file(SCAN_type,STBI_default); + png p; + start_mem(&p.s, buffer, len); + return parse_png_file(&p, SCAN_type,STBI_default); } // TODO: load header from png -extern int stbi_png_info (char *filename, int *x, int *y, int *comp); +#ifndef STBI_NO_STDIO +extern int stbi_png_info (char const *filename, int *x, int *y, int *comp); extern int stbi_png_info_from_file (FILE *f, int *x, int *y, int *comp); -extern int stbi_png_info_from_memory (stbi_uc *buffer, int len, int *x, int *y, int *comp); +#endif +extern int stbi_png_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp); // Microsoft/Windows BMP image -static int bmp_test(void) +static int bmp_test(stbi *s) { int sz; - if (get8() != 'B') return 0; - if (get8() != 'M') return 0; - get32le(); // discard filesize - get16le(); // discard reserved - get16le(); // discard reserved - get32le(); // discard data offset - sz = get32le(); + 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; } @@ -2513,18 +2724,20 @@ static int bmp_test(void) #ifndef STBI_NO_STDIO int stbi_bmp_test_file (FILE *f) { + stbi s; int r,n = ftell(f); - start_file(f); - r = bmp_test(); + start_file(&s,f); + r = bmp_test(&s); fseek(f,n,SEEK_SET); return r; } #endif -int stbi_bmp_test_memory (stbi_uc *buffer, int len) +int stbi_bmp_test_memory (stbi_uc const *buffer, int len) { - start_mem(buffer, len); - return bmp_test(); + stbi s; + start_mem(&s, buffer, len); + return bmp_test(&s); } // returns 0..31 for the highest set bit @@ -2567,49 +2780,50 @@ static int shiftsigned(int v, int shift, int bits) return result; } -static stbi_uc *bmp_load(int *x, int *y, int *comp, int req_comp) +static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp) { + uint8 *out; unsigned int mr=0,mg=0,mb=0,ma=0; stbi_uc pal[256][4]; int psize=0,i,j,compress=0,width; int bpp, flip_vertically, pad, target, offset, hsz; - if (get8() != 'B' || get8() != 'M') return ep("not BMP", "Corrupt BMP"); - get32le(); // discard filesize - get16le(); // discard reserved - get16le(); // discard reserved - offset = get32le(); - hsz = get32le(); - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return ep("unknown BMP", "BMP type not supported: unknown"); + 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"); failure_reason = "bad BMP"; if (hsz == 12) { - img_x = get16le(); - img_y = get16le(); + s->img_x = get16le(s); + s->img_y = get16le(s); } else { - img_x = get32le(); - img_y = get32le(); + s->img_x = get32le(s); + s->img_y = get32le(s); } - if (get16le() != 1) return 0; - bpp = get16le(); - if (bpp == 1) return ep("monochrome", "BMP type not supported: 1-bit"); - flip_vertically = ((int) img_y) > 0; - img_y = abs((int) img_y); + if (get16le(s) != 1) return 0; + 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(); - if (compress == 1 || compress == 2) return ep("BMP RLE", "BMP type not supported: RLE"); - get32le(); // discard sizeof - get32le(); // discard hres - get32le(); // discard vres - get32le(); // discard colorsused - get32le(); // discard max important + 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(); - get32le(); - get32le(); - get32le(); + get32le(s); + get32le(s); + get32le(s); + get32le(s); } if (bpp == 16 || bpp == 32) { mr = mg = mb = 0; @@ -2624,9 +2838,9 @@ static stbi_uc *bmp_load(int *x, int *y, int *comp, int req_comp) mb = 31 << 0; } } else if (compress == 3) { - mr = get32le(); - mg = get32le(); - mb = get32le(); + mr = get32le(s); + mg = get32le(s); + mb = get32le(s); // not documented, but generated by photoshop and handled by mspaint if (mr == mg && mg == mb) { // ?!?!? @@ -2637,42 +2851,42 @@ static stbi_uc *bmp_load(int *x, int *y, int *comp, int req_comp) } } else { assert(hsz == 108); - mr = get32le(); - mg = get32le(); - mb = get32le(); - ma = get32le(); - get32le(); // discard color space + mr = get32le(s); + mg = get32le(s); + mb = get32le(s); + ma = get32le(s); + get32le(s); // discard color space for (i=0; i < 12; ++i) - get32le(); // discard color space parameters + get32le(s); // discard color space parameters } if (bpp < 16) psize = (offset - 14 - hsz) >> 2; } - img_n = ma ? 4 : 3; + s->img_n = ma ? 4 : 3; if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 target = req_comp; else - target = img_n; // if they want monochrome, we'll post-convert - out = (stbi_uc *) malloc(target * img_x * img_y); - if (!out) return ep("outofmem", "Out of memory"); + 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) return ep("invalid", "Corrupt BMP"); + if (psize == 0 || psize > 256) { free(out); return epuc("invalid", "Corrupt BMP"); } for (i=0; i < psize; ++i) { - pal[i][2] = get8(); - pal[i][1] = get8(); - pal[i][0] = get8(); - if (hsz != 12) get8(); + pal[i][2] = get8(s); + pal[i][1] = get8(s); + pal[i][0] = get8(s); + if (hsz != 12) get8(s); pal[i][3] = 255; } - skip(offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); - if (bpp == 4) width = (img_x + 1) >> 1; - else if (bpp == 8) width = img_x; - else return ep("bad bpp", "Corrupt BMP"); + 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) img_y; ++j) { - for (i=0; i < (int) img_x; i += 2) { - int v=get8(),v2=0; + 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; @@ -2681,22 +2895,22 @@ static stbi_uc *bmp_load(int *x, int *y, int *comp, int req_comp) out[z++] = pal[v][1]; out[z++] = pal[v][2]; if (target == 4) out[z++] = 255; - if (i+1 == (int) img_x) break; - v = (bpp == 8) ? get8() : v2; + 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(pad); + 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(offset - 14 - hsz); - if (bpp == 24) width = 3 * img_x; - else if (bpp == 16) width = 2*img_x; + 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) { @@ -2706,27 +2920,27 @@ static stbi_uc *bmp_load(int *x, int *y, int *comp, int req_comp) easy = 2; } if (!easy) { - if (!mr || !mg || !mb) return ep("bad masks", "Corrupt BMP"); + if (!mr || !mg || !mb) 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(mr); bshift = high_bit(mb)-7; bcount = bitcount(mr); ashift = high_bit(ma)-7; acount = bitcount(mr); } - for (j=0; j < (int) img_y; ++j) { + for (j=0; j < (int) s->img_y; ++j) { if (easy) { - for (i=0; i < (int) img_x; ++i) { + for (i=0; i < (int) s->img_x; ++i) { int a; - out[z+2] = get8(); - out[z+1] = get8(); - out[z+0] = get8(); + out[z+2] = get8(s); + out[z+1] = get8(s); + out[z+0] = get8(s); z += 3; - a = (easy == 2 ? get8() : 255); + a = (easy == 2 ? get8(s) : 255); if (target == 4) out[z++] = a; } } else { - for (i=0; i < (int) img_x; ++i) { - unsigned long v = (bpp == 16 ? get16le() : get32le()); + for (i=0; i < (int) s->img_x; ++i) { + uint32 v = (bpp == 16 ? get16le(s) : get32le(s)); int a; out[z++] = shiftsigned(v & mr, rshift, rcount); out[z++] = shiftsigned(v & mg, gshift, gcount); @@ -2735,33 +2949,33 @@ static stbi_uc *bmp_load(int *x, int *y, int *comp, int req_comp) if (target == 4) out[z++] = a; } } - skip(pad); + skip(s, pad); } } if (flip_vertically) { stbi_uc t; - for (j=0; j < (int) img_y>>1; ++j) { - stbi_uc *p1 = out + j *img_x*target; - stbi_uc *p2 = out + (img_y-1-j)*img_x*target; - for (i=0; i < (int) img_x*target; ++i) { + 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); + 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 = img_x; - *y = img_y; + *x = s->img_x; + *y = s->img_y; if (comp) *comp = target; return out; } #ifndef STBI_NO_STDIO -stbi_uc *stbi_bmp_load (char *filename, int *x, int *y, int *comp, int req_comp) +stbi_uc *stbi_bmp_load (char const *filename, int *x, int *y, int *comp, int req_comp) { stbi_uc *data; FILE *f = fopen(filename, "rb"); @@ -2773,36 +2987,38 @@ stbi_uc *stbi_bmp_load (char *filename, int *x, int *y, in stbi_uc *stbi_bmp_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp) { - start_file(f); - return bmp_load(x,y,comp,req_comp); + stbi s; + start_file(&s, f); + return bmp_load(&s, x,y,comp,req_comp); } #endif -stbi_uc *stbi_bmp_load_from_memory (stbi_uc *buffer, int len, int *x, int *y, int *comp, int req_comp) +stbi_uc *stbi_bmp_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { - start_mem(buffer, len); - return bmp_load(x,y,comp,req_comp); + stbi s; + start_mem(&s, buffer, len); + return bmp_load(&s, x,y,comp,req_comp); } // Targa Truevision - TGA // by Jonathan Dummer -static int tga_test(void) +static int tga_test(stbi *s) { int sz; - get8u(); // discard Offset - sz = get8u(); // color type + get8u(s); // discard Offset + sz = get8u(s); // color type if( sz > 1 ) return 0; // only RGB or indexed allowed - sz = get8u(); // image type + 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(); // discard palette start - get16(); // discard palette length - get8(); // discard bits per palette color entry - get16(); // discard x origin - get16(); // discard y origin - if( get16() < 1 ) return 0; // test width - if( get16() < 1 ) return 0; // test height - sz = get8(); // bits per pixel + 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 } @@ -2810,36 +3026,38 @@ static int tga_test(void) #ifndef STBI_NO_STDIO int stbi_tga_test_file (FILE *f) { + stbi s; int r,n = ftell(f); - start_file(f); - r = tga_test(); + start_file(&s, f); + r = tga_test(&s); fseek(f,n,SEEK_SET); return r; } #endif -int stbi_tga_test_memory (stbi_uc *buffer, int len) +int stbi_tga_test_memory (stbi_uc const *buffer, int len) { - start_mem(buffer, len); - return tga_test(); + stbi s; + start_mem(&s, buffer, len); + return tga_test(&s); } -static stbi_uc *tga_load(int *x, int *y, int *comp, int req_comp) +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(); - int tga_indexed = get8u(); - int tga_image_type = get8u(); + 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(); - int tga_palette_len = get16le(); - int tga_palette_bits = get8u(); - int tga_x_origin = get16le(); - int tga_y_origin = get16le(); - int tga_width = get16le(); - int tga_height = get16le(); - int tga_bits_per_pixel = get8u(); - int tga_inverted = get8u(); + 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_inverted = get8u(s); // image data unsigned char *tga_data; unsigned char *tga_palette = NULL; @@ -2886,20 +3104,20 @@ static stbi_uc *tga_load(int *x, int *y, int *comp, int req_comp) } else { // force a new number of components - *comp = req_comp; + *comp = tga_bits_per_pixel/8; } tga_data = (unsigned char*)malloc( tga_width * tga_height * req_comp ); // skip to the data's starting position (offset usually = 0) - skip( tga_offset ); + skip(s, tga_offset ); // do I need to load a palette? if( tga_indexed ) { // any data to skip? (offset usually = 0) - skip( tga_palette_start ); + skip(s, tga_palette_start ); // load the palette tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 ); - getn( tga_palette, tga_palette_len * tga_palette_bits / 8 ); + getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 ); } // load the data for( i = 0; i < tga_width * tga_height; ++i ) @@ -2910,7 +3128,7 @@ static stbi_uc *tga_load(int *x, int *y, int *comp, int req_comp) if( RLE_count == 0 ) { // yep, get the next byte as a RLE command - int RLE_cmd = get8u(); + int RLE_cmd = get8u(s); RLE_count = 1 + (RLE_cmd & 127); RLE_repeating = RLE_cmd >> 7; read_next_pixel = 1; @@ -2929,7 +3147,7 @@ static stbi_uc *tga_load(int *x, int *y, int *comp, int req_comp) if( tga_indexed ) { // read in 1 byte, then perform the lookup - int pal_idx = get8u(); + int pal_idx = get8u(s); if( pal_idx >= tga_palette_len ) { // invalid index @@ -2945,7 +3163,7 @@ static stbi_uc *tga_load(int *x, int *y, int *comp, int req_comp) // read in the data raw for( j = 0; j*8 < tga_bits_per_pixel; ++j ) { - raw_data[j] = get8u(); + raw_data[j] = get8u(s); } } // convert raw to the intermediate format @@ -2987,12 +3205,12 @@ static stbi_uc *tga_load(int *x, int *y, int *comp, int req_comp) switch( req_comp ) { case 1: - // RGBA => Luminous - tga_data[i*req_comp+0] = (trans_data[0] + trans_data[1] + trans_data[2]) / 3; + // RGBA => Luminance + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); break; case 2: - // RGBA => Luminous,Alpha - tga_data[i*req_comp+0] = (trans_data[0] + trans_data[1] + trans_data[2]) / 3; + // RGBA => Luminance,Alpha + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); tga_data[i*req_comp+1] = trans_data[3]; break; case 3: @@ -3043,7 +3261,7 @@ static stbi_uc *tga_load(int *x, int *y, int *comp, int req_comp) } #ifndef STBI_NO_STDIO -stbi_uc *stbi_tga_load (char *filename, int *x, int *y, int *comp, int req_comp) +stbi_uc *stbi_tga_load (char const *filename, int *x, int *y, int *comp, int req_comp) { stbi_uc *data; FILE *f = fopen(filename, "rb"); @@ -3055,65 +3273,284 @@ stbi_uc *stbi_tga_load (char *filename, int *x, int *y, in stbi_uc *stbi_tga_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp) { - start_file(f); - return tga_load(x,y,comp,req_comp); + stbi s; + start_file(&s, f); + return tga_load(&s, x,y,comp,req_comp); } #endif -stbi_uc *stbi_tga_load_from_memory (stbi_uc *buffer, int len, int *x, int *y, int *comp, int req_comp) +stbi_uc *stbi_tga_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { - start_mem(buffer, len); - return tga_load(x,y,comp,req_comp); + stbi s; + start_mem(&s, buffer, len); + return tga_load(&s, x,y,comp,req_comp); } + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicholas Schulz, tweaked by STB + +static int psd_test(stbi *s) +{ + if (get32(s) != 0x38425053) return 0; // "8BPS" + else return 1; +} + +#ifndef STBI_NO_STDIO +int stbi_psd_test_file(FILE *f) +{ + stbi s; + int r,n = ftell(f); + start_file(&s, f); + r = psd_test(&s); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +int stbi_psd_test_memory(stbi_uc const *buffer, int len) +{ + stbi s; + start_mem(&s, buffer, len); + return psd_test(&s); +} + +static stbi_uc *psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + int pixelCount; + int channelCount, compression; + int channel, i, count, len; + int w,h; + uint8 *out; + + // Check identifier + if (get32(s) != 0x38425053) // "8BPS" + return epuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (get16(s) != 1) + return epuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = get16(s); + if (channelCount < 0 || channelCount > 16) + return epuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = get32(s); + w = get32(s); + + // Make sure the depth is 8 bits. + if (get16(s) != 8) + return epuc("unsupported bit depth", "PSD bit depth is not 8 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (get16(s) != 3) + return epuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + skip(s,get32(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + skip(s, get32(s) ); + + // Skip the reserved data. + skip(s, get32(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = get16(s); + if (compression > 1) + return epuc("bad compression", "PSD has an unknown compression format"); + + // Create the destination image. + out = (stbi_uc *) malloc(4 * w*h); + if (!out) return epuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; + } else { + // Read the RLE data. + count = 0; + while (count < pixelCount) { + len = get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + count += len; + while (len) { + *p = get8(s); + p += 4; + len--; + } + } else if (len > 128) { + uint32 val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0x0FF; + len += 2; + val = get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out + channel; + if (channel > channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; + } else { + // Read the data. + count = 0; + for (i = 0; i < pixelCount; i++) + *p = get8(s), p += 4; + } + } + } + + if (req_comp && req_comp != 4) { + out = convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // convert_format frees input on failure + } + + if (comp) *comp = channelCount; + *y = h; + *x = w; + + return out; +} + +#ifndef STBI_NO_STDIO +stbi_uc *stbi_psd_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_psd_load_from_file(f, x,y,comp,req_comp); + fclose(f); + return data; +} + +stbi_uc *stbi_psd_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s, f); + return psd_load(&s, x,y,comp,req_comp); +} +#endif + +stbi_uc *stbi_psd_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 psd_load(&s, x,y,comp,req_comp); +} + + // ************************************************************************************************* // Radiance RGBE HDR loader // originally by Nicolas Schulz #ifndef STBI_NO_HDR -static int hdr_test(void) +static int hdr_test(stbi *s) { char *signature = "#?RADIANCE\n"; int i; for (i=0; signature[i]; ++i) - if (get8() != signature[i]) + if (get8(s) != signature[i]) return 0; return 1; } -int stbi_hdr_test_memory(stbi_uc *buffer, int len) +int stbi_hdr_test_memory(stbi_uc const *buffer, int len) { - start_mem(buffer, len); - return hdr_test(); + stbi s; + start_mem(&s, buffer, len); + return hdr_test(&s); } #ifndef STBI_NO_STDIO int stbi_hdr_test_file(FILE *f) { + stbi s; int r,n = ftell(f); - start_file(f); - r = hdr_test(); + start_file(&s, f); + r = hdr_test(&s); fseek(f,n,SEEK_SET); return r; } #endif #define HDR_BUFLEN 1024 -static char *hdr_gettoken(char *buffer) +static char *hdr_gettoken(stbi *z, char *buffer) { int len=0; char *s = buffer, c = '\0'; - c = get8(); + c = get8(z); - while (!at_eof() && c != '\n') { + while (!at_eof(z) && c != '\n') { buffer[len++] = c; if (len == HDR_BUFLEN-1) { // flush to end of line - while (!at_eof() && get8() != '\n') + while (!at_eof(z) && get8(z) != '\n') ; break; } - c = get8(); + c = get8(z); } buffer[len] = 0; @@ -3137,10 +3574,10 @@ static void hdr_convert(float *output, stbi_uc *input, int req_comp) if (req_comp == 4) output[3] = 1; } else { switch (req_comp) { - case 4: output[3] = 255; /* fallthrough */ + case 4: output[3] = 1; /* fallthrough */ case 3: output[0] = output[1] = output[2] = 0; break; - case 2: output[1] = 255; /* fallthrough */ + case 2: output[1] = 1; /* fallthrough */ case 1: output[0] = 0; break; } @@ -3148,7 +3585,7 @@ static void hdr_convert(float *output, stbi_uc *input, int req_comp) } -static float *hdr_load(int *x, int *y, int *comp, int req_comp) +static float *hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) { char buffer[HDR_BUFLEN]; char *token; @@ -3162,26 +3599,26 @@ static float *hdr_load(int *x, int *y, int *comp, int req_comp) // Check identifier - if (strcmp(hdr_gettoken(buffer), "#?RADIANCE") != 0) - return ep("not HDR", "Corrupt HDR image"); + if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + return epf("not HDR", "Corrupt HDR image"); // Parse header while(1) { - token = hdr_gettoken(buffer); + token = hdr_gettoken(s,buffer); if (token[0] == 0) break; if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; } - if (!valid) return ep("unsupported format", "Unsupported HDR format"); + 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(buffer); - if (strncmp(token, "-Y ", 3)) return ep("unsupported data layout", "Unsupported HDR format"); + token = hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); token += 3; height = strtol(token, &token, 10); while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) return ep("unsupported data layout", "Unsupported HDR format"); + if (strncmp(token, "+X ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); token += 3; width = strtol(token, NULL, 10); @@ -3202,7 +3639,7 @@ static float *hdr_load(int *x, int *y, int *comp, int req_comp) for (i=0; i < width; ++i) { stbi_uc rgbe[4]; main_decode_loop: - getn(rgbe, 4); + getn(s, rgbe, 4); hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); } } @@ -3211,13 +3648,13 @@ static float *hdr_load(int *x, int *y, int *comp, int req_comp) scanline = NULL; for (j = 0; j < height; ++j) { - c1 = get8(); - c2 = get8(); - len = get8(); + 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_uc rgbe[4] = { c1,c2,len, get8() }; + stbi_uc rgbe[4] = { c1,c2,len, get8(s) }; hdr_convert(hdr_data, rgbe, req_comp); i = 1; j = 0; @@ -3225,24 +3662,24 @@ static float *hdr_load(int *x, int *y, int *comp, int req_comp) goto main_decode_loop; // yes, this is fucking insane; blame the fucking insane format } len <<= 8; - len |= get8(); - if (len != width) { free(hdr_data); free(scanline); return ep("invalid decoded scanline length", "corrupt HDR"); } + 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 = get8(); + count = get8(s); if (count > 128) { // Run - value = get8(); + value = get8(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] = get8(); + scanline[i++ * 4 + k] = get8(s); } } } @@ -3258,15 +3695,17 @@ static float *hdr_load(int *x, int *y, int *comp, int req_comp) #ifndef STBI_NO_STDIO float *stbi_hdr_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) { - start_file(f); - return hdr_load(x,y,comp,req_comp); + stbi s; + start_file(&s,f); + return hdr_load(&s,x,y,comp,req_comp); } #endif -float *stbi_hdr_load_from_memory(stbi_uc *buffer, int len, int *x, int *y, int *comp, int req_comp) +float *stbi_hdr_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { - start_mem(buffer, len); - return hdr_load(x,y,comp,req_comp); + stbi s; + start_mem(&s,buffer, len); + return hdr_load(&s,x,y,comp,req_comp); } #endif // STBI_NO_HDR @@ -3340,7 +3779,7 @@ static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, } } -static int outfile(char *filename, int rgb_dir, int vdir, int x, int y, int comp, void *data, int alpha, int pad, char *fmt, ...) +static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, void *data, int alpha, int pad, char *fmt, ...) { FILE *f = fopen(filename, "wb"); if (f) { @@ -3354,7 +3793,7 @@ static int outfile(char *filename, int rgb_dir, int vdir, int x, int y, int comp return f != NULL; } -int stbi_write_bmp(char *filename, int x, int y, int comp, void *data) +int stbi_write_bmp(char const *filename, int x, int y, int comp, void *data) { int pad = (-x*3) & 3; return outfile(filename,-1,-1,x,y,comp,data,0,pad, @@ -3363,7 +3802,7 @@ int stbi_write_bmp(char *filename, int x, int y, int comp, void *data) 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header } -int stbi_write_tga(char *filename, int x, int y, int comp, void *data) +int stbi_write_tga(char const *filename, int x, int y, int comp, void *data) { int has_alpha = !(comp & 1); return outfile(filename, -1,-1, x, y, comp, data, has_alpha, 0, @@ -3375,4 +3814,7 @@ int stbi_write_tga(char *filename, int x, int y, int comp, void *data) // PSD: no, channels output separately // TIFF: no, stripwise-interleaved... i think -#endif STBI_NO_WRITE +#endif // STBI_NO_WRITE + +#endif // STBI_HEADER_FILE_ONLY + diff --git a/vc6/stb_imv.dsp b/vc6/stb_imv.dsp index eccae33..897c216 100644 --- a/vc6/stb_imv.dsp +++ b/vc6/stb_imv.dsp @@ -21,6 +21,7 @@ CFG=stb_imv - Win32 Debug !MESSAGE "stb_imv - Win32 Debug" (based on "Win32 (x86) Application") !MESSAGE "stb_imv - Win32 Debug Opt" (based on "Win32 (x86) Application") !MESSAGE "stb_imv - Win32 Light Release" (based on "Win32 (x86) Application") +!MESSAGE "stb_imv - Win32 Release Static" (based on "Win32 (x86) Application") !MESSAGE # Begin Project @@ -45,7 +46,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /G6 /MD /W3 /GX /O2 /Ob0 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /FD /c +# ADD CPP /nologo /G6 /MD /W3 /GX /Zi /O2 /Ob0 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -56,7 +57,8 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib advapi32.lib winmm.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib advapi32.lib winmm.lib /nologo /subsystem:windows /map /machine:I386 +# SUBTRACT LINK32 /debug !ELSEIF "$(CFG)" == "stb_imv - Win32 Debug" @@ -72,7 +74,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /G6 /MTd /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# ADD CPP /nologo /G6 /MTd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 @@ -142,6 +144,36 @@ LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib advapi32.lib winmm.lib /nologo /subsystem:windows /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib advapi32.lib winmm.lib /nologo /subsystem:windows /machine:I386 /out:"Light_Release/stb_imv_light.exe" +!ELSEIF "$(CFG)" == "stb_imv - Win32 Release Static" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "stb_imv___Win32_Release_Static" +# PROP BASE Intermediate_Dir "stb_imv___Win32_Release_Static" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "stb_imv___Win32_Release_Static" +# PROP Intermediate_Dir "stb_imv___Win32_Release_Static" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /G6 /MD /W3 /GX /Zi /O2 /Ob0 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /FD /c +# SUBTRACT BASE CPP /YX +# ADD CPP /nologo /G6 /MT /W3 /GX /O2 /Ob0 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib advapi32.lib winmm.lib /nologo /subsystem:windows /debug /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib advapi32.lib winmm.lib /nologo /subsystem:windows /machine:I386 +# SUBTRACT LINK32 /debug + !ENDIF # Begin Target @@ -150,6 +182,7 @@ LINK32=link.exe # Name "stb_imv - Win32 Debug" # Name "stb_imv - Win32 Debug Opt" # Name "stb_imv - Win32 Light Release" +# Name "stb_imv - Win32 Release Static" # Begin Source File SOURCE=..\imv.c @@ -180,6 +213,11 @@ SOURCE=..\notes.txt # PROP BASE Exclude_From_Build 1 # PROP Exclude_From_Build 1 +!ELSEIF "$(CFG)" == "stb_imv - Win32 Release Static" + +# PROP BASE Exclude_From_Build 1 +# PROP Exclude_From_Build 1 + !ENDIF # End Source File @@ -217,6 +255,11 @@ SOURCE=..\version.bat # PROP BASE Exclude_From_Build 1 # PROP Exclude_From_Build 1 +!ELSEIF "$(CFG)" == "stb_imv - Win32 Release Static" + +# PROP BASE Exclude_From_Build 1 +# PROP Exclude_From_Build 1 + !ENDIF # End Source File diff --git a/version.bat b/version.bat index 9ad2aba..e68f2cc 100644 --- a/version.bat +++ b/version.bat @@ -1 +1 @@ -set VERSION="0.96" +set VERSION="0.99"