diff --git a/imv.c b/imv.c index dd4a735..a9b30e7 100644 --- a/imv.c +++ b/imv.c @@ -343,6 +343,7 @@ static unsigned char alpha_background[2][3] = // given raw decoded data from stbi_load, make it into a proper Image (e.g. creating a // windows-compatible bitmap with 4-byte aligned rows and BGR color order) +float lmin=0,lmax=1; 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; @@ -363,6 +364,14 @@ void make_image(Image *z, int image_x, int image_y, uint8 *image_data, BOOL imag image_data[k+2] = t; } + 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); + image_data[k+c] = stb_clamp(z, 0, 255); + } + } + #if BPP==4 // if image had an alpha channel, pre-blend with background if (image_n == 4) { @@ -1235,7 +1244,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\0"; +char *open_filter = "Image Files\0*.jpg;*.jpeg;*.png;*.bmp;*.tga;*.hdr\0"; // build a filelist for the current directory void init_filelist(void) @@ -1881,6 +1890,27 @@ static void dialog_clampf(int id, float low, float high) extern unsigned char *rom_images[]; // preference images +void clear_cache(int had_alpha) +{ + int i; + stb_mutex_begin(cache_mutex); + for (i=0; i < MAX_CACHED_IMAGES; ++i) { + if (cache[i].status == LOAD_available) { + if (had_alpha ? cache[i].image->had_alpha : TRUE) { + stb_sdict_remove(file_cache, cache[i].filename, NULL); + free(cache[i].filename); + imfree(cache[i].image); + cache[i].status = LOAD_unused; + cache[i].image = NULL; + cache[i].filename = NULL; + } + } + } + stb_mutex_end(cache_mutex); + free(cur_filename); + cur_filename = NULL; +} + // preferences dialog windows procedure BOOL CALLBACK PrefDlgProc(HWND hdlg, UINT imsg, WPARAM wparam, LPARAM lparam) { @@ -1977,20 +2007,7 @@ BOOL CALLBACK PrefDlgProc(HWND hdlg, UINT imsg, WPARAM wparam, LPARAM lparam) // if alpha_background changed, clear the cache of any images that used it if (memcmp(alpha_background, curc, 6)) { - stb_mutex_begin(cache_mutex); - for (i=0; i < MAX_CACHED_IMAGES; ++i) { - if (cache[i].status == LOAD_available) { - if (cache[i].image->had_alpha) { - stb_sdict_remove(file_cache, cache[i].filename, NULL); - free(cache[i].filename); - imfree(cache[i].image); - cache[i].status = LOAD_unused; - cache[i].image = NULL; - cache[i].filename = NULL; - } - } - } - stb_mutex_end(cache_mutex); + clear_cache(1); // force a reload of the current image advance(0); } else if (old_cubic != upsample_cubic) { @@ -2217,6 +2234,11 @@ int WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) KillTimer(win,0); break; + 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; + default: return 1; } @@ -2422,7 +2444,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine #ifdef USE_GDIPLUS if (LoadGdiplus()) { strcat(helptext_center, "\nUsing GDI+"); - open_filter = "Image Files\0*.jpg;*.jpeg;*.png;*.bmp;*.tga;*.gif;*.ico;*.jng;*.tiff\0"; + open_filter = "Image Files\0*.jpg;*.jpeg;*.png;*.bmp;*.tga;*.hdr;*.gif;*.ico;*.jng;*.tiff\0"; } #endif @@ -2431,7 +2453,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine #ifdef USE_FREEIMAGE if (LoadFreeImage()) { strcat(helptext_center, "\nUsing FreeImage.dll: http://freeimage.sourceforge.net"); - open_filter = "Image Files\0*.jpg;*.jpeg;*.png;*.bmp;*.dds;*.gif;*.ico;*.jng;*.lbm;*.pcx;*.ppm;*.psd;*.tga;*.tiff\0"; + open_filter = "Image Files\0*.jpg;*.jpeg;*.png;*.bmp;*.hdr;*.dds;*.gif;*.ico;*.jng;*.lbm;*.pcx;*.ppm;*.psd;*.tga;*.tiff\0"; } #endif assert(helptext_center[sizeof(helptext_center)-1]==0); diff --git a/stb_image.c b/stb_image.c index 97503f5..ebfc643 100644 --- a/stb_image.c +++ b/stb_image.c @@ -1,4 +1,4 @@ -/* stbi-0.98 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c +/* stbi-1.02 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c when you control the images you're loading QUICK NOTES: @@ -8,6 +8,8 @@ JPEG baseline (no JPEG progressive, no oddball channel decimations) PNG non-interlaced BMP non-1bpp, non-RLE + TGA (not sure what subset, if a subset) + 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) @@ -16,7 +18,12 @@ PSD loader history: - 0.98 TGA loader by lonesock; dynamically add loaders + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) 0.97 jpeg errors on too large a file; also catch another malloc failure 0.96 fix detection of invalid v value - particleman@mollyrocket forum 0.95 during header scan, seek to markers in case of padding @@ -50,7 +57,7 @@ // - channel subsampling of at most 2 in each dimension (jpeg) // - no delayed line count (jpeg) -- IJG doesn't support either // -// Basic usage: +// Basic usage (see HDR discussion below): // int x,y,n; // unsigned char *data = stbi_load(filename, &x, &y, &n, 0); // // ... process data if not NULL ... @@ -91,11 +98,54 @@ // more user-friendly ones. // // Paletted PNG and BMP images are automatically depalettized. +// +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image now supports loading HDR images in general, and currently +// the Radiance .HDR file format, although the support is provided +// generically. You can still load any file through the existing interface; +// if you attempt to load an HDR file, it will be automatically remapped to +// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); + #ifndef STBI_NO_STDIO #include #endif +#ifndef STBI_NO_HDR +#include // ldexp +#include // strcmp +#endif + enum { STBI_default = 0, // only used for req_comp @@ -133,6 +183,21 @@ extern int stbi_info_from_file (FILE *f, int *x, int *y, extern stbi_uc *stbi_load_from_memory(stbi_uc *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_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 void stbi_hdr_to_ldr_gamma(float gamma); +extern void stbi_hdr_to_ldr_scale(float scale); + +extern void stbi_ldr_to_hdr_gamma(float gamma); +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); @@ -141,8 +206,11 @@ extern void stbi_image_free (stbi_uc *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); #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_is_hdr_from_file(FILE *f); #endif // ZLIB client - used by PNG, available for other purposes @@ -151,6 +219,9 @@ 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); + // TYPE-SPECIFIC ACCESS @@ -203,6 +274,16 @@ 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); + +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); +#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); +#endif + // define new loaders typedef struct { @@ -279,7 +360,7 @@ static int e(char *str) #define e(x,y) e(x) #endif -#define ep(x,y) (e(x,y),NULL) +#define ep(x,y) (e(x,y)?NULL:NULL) void stbi_image_free(unsigned char *retval_from_stbi_load) { @@ -308,6 +389,11 @@ int stbi_register_loader(stbi_loader *loader) return 0; } +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp); +#endif + #ifndef STBI_NO_STDIO unsigned char *stbi_load(char *filename, int *x, int *y, int *comp, int req_comp) { @@ -328,11 +414,18 @@ 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_tga_test_file(f)) - return stbi_tga_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); + return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif for (i=0; i < max_loaders; ++i) if (loaders[i]->test_file(f)) return loaders[i]->load_from_file(f,x,y,comp,req_comp); + // 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"); } #endif @@ -346,19 +439,103 @@ 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_tga_test_memory(buffer,len)) - return stbi_tga_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); + return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif for (i=0; i < max_loaders; ++i) if (loaders[i]->test_memory(buffer,len)) return loaders[i]->load_from_memory(buffer,len,x,y,comp,req_comp); + // 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"); } +#ifndef STBI_NO_HDR + +#ifndef STBI_NO_STDIO +float *stbi_loadf(char *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"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + if (stbi_hdr_test_file(f)) + return stbi_hdr_load_from_file(f,x,y,comp,req_comp); + 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"); +} +#endif + +float *stbi_loadf_from_memory(stbi_uc *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *data; + if (stbi_hdr_test_memory(buffer, len)) + return stbi_hdr_load_from_memory(buffer, len,x,y,comp,req_comp); + 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"); +} +#endif + +// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is +// defined, for API simplicity; if STBI_NO_HDR is defined, it always +// reports false! + +extern int stbi_is_hdr_from_memory(stbi_uc *buffer, int len) +{ + return stbi_hdr_test_memory(buffer, len); +} + +#ifndef STBI_NO_STDIO +extern int stbi_is_hdr (char *filename) +{ + FILE *f = fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +extern int stbi_is_hdr_from_file(FILE *f) +{ + return stbi_hdr_test_file(f); +} + +#endif + // @TODO: get image dimensions & components without fully decoding extern int stbi_info (char *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); +#ifndef STBI_NO_HDR +static float h2l_gamma_i=1.0f/2.2f, h2l_scale_i=1.0f; +static float l2h_gamma=2.2f, l2h_scale=1.0f; + +void stbi_hdr_to_ldr_gamma(float gamma) { h2l_gamma_i = 1/gamma; } +void stbi_hdr_to_ldr_scale(float scale) { h2l_scale_i = 1/scale; } + +void stbi_ldr_to_hdr_gamma(float gamma) { l2h_gamma = gamma; } +void stbi_ldr_to_hdr_scale(float scale) { l2h_scale = scale; } +#endif + + ////////////////////////////////////////////////////////////////////////////// // // Common code used by all image loaders @@ -376,7 +553,6 @@ enum }; // An API for reading either from memory or file. -// It fits on a single screen. No abstract base classes needed. #ifndef STBI_NO_STDIO static FILE *img_file; #endif @@ -508,7 +684,8 @@ static unsigned char *convert_format(unsigned char *data, int img_n, int req_com #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) - // convert source image with img_n components to one with req_comp components + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros switch(COMBO(img_n, req_comp)) { CASE(1,2) dest[0]=src[0], dest[1]=255; break; CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; @@ -532,6 +709,49 @@ static unsigned char *convert_format(unsigned char *data, int img_n, int req_com return good; } +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"); } + // 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; + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + free(data); + return output; +} + +#define float2int(x) ((int) (x)) +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output = (stbi_uc *) malloc(x * y * comp); + if (output == NULL) { free(data); return ep("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*h2l_scale_i, h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = float2int(z); + } + } + free(data); + return output; +} + ////////////////////////////////////////////////////////////////////////////// // // "baseline" JPEG/JFIF decoder (not actually fully baseline implementation) @@ -1777,10 +1997,11 @@ static void init_defaults(void) for (i=0; i <= 31; ++i) default_distance[i] = 5; } -static int parse_zlib(void) +static int parse_zlib(int parse_header) { int final, type; - if (!parse_zlib_header()) return 0; + if (parse_header) + if (!parse_zlib_header()) return 0; num_bits = 0; code_buffer = 0; do { @@ -1805,21 +2026,21 @@ static int parse_zlib(void) return 1; } -static int do_zlib(char *obuf, int olen, int exp) +static int do_zlib(char *obuf, int olen, int exp, int parse_header) { zout_start = obuf; zout = obuf; zout_end = obuf + olen; z_expandable = exp; - return parse_zlib(); + return parse_zlib(parse_header); } char *stbi_zlib_decode_malloc_guesssize(int initial_size, int *outlen) { char *p = (char *) malloc(initial_size); if (p == NULL) return NULL; - if (do_zlib(p, initial_size, 1)) { + if (do_zlib(p, initial_size, 1, 1)) { *outlen = (int) (zout - zout_start); return zout_start; } else { @@ -1839,11 +2060,37 @@ int stbi_zlib_decode_buffer(char *obuffer, int olen, char *ibuffer, int ilen) { zbuffer = (uint8 *) ibuffer; zbuffer_end = (uint8 *) ibuffer + ilen; - if (do_zlib(obuffer, olen, 0)) + if (do_zlib(obuffer, olen, 0, 1)) return (int) (zout - zout_start); else return -1; } + +char *stbi_zlib_decode_noheader_malloc(char *buffer, int len, int *outlen) +{ + 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; + } else { + free(zout_start); + return NULL; + } +} + +int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, char *ibuffer, int ilen) +{ + zbuffer = (uint8 *) ibuffer; + zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(obuffer, olen, 0, 0)) + return (int) (zout - zout_start); + else + return -1; +} + // public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 // simple implementation // - only 8-bit samples @@ -2093,6 +2340,7 @@ static int parse_png_file(int scan, int req_comp) if (scan == SCAN_header) { 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(); } else { @@ -2343,8 +2591,8 @@ static stbi_uc *bmp_load(int *x, int *y, int *comp, int req_comp) if (get16le() != 1) return 0; bpp = get16le(); if (bpp == 1) return ep("monochrome", "BMP type not supported: 1-bit"); - flip_vertically = img_y > 0; - img_y = abs(img_y); + flip_vertically = ((int) img_y) > 0; + img_y = abs((int) img_y); if (hsz == 12) { if (bpp < 24) psize = (offset - 14 - 24) / 3; @@ -2518,7 +2766,7 @@ stbi_uc *stbi_bmp_load (char *filename, int *x, int *y, in stbi_uc *data; FILE *f = fopen(filename, "rb"); if (!f) return NULL; - data = bmp_load(x,y,comp,req_comp); + data = stbi_bmp_load_from_file(f, x,y,comp,req_comp); fclose(f); return data; } @@ -2800,7 +3048,7 @@ stbi_uc *stbi_tga_load (char *filename, int *x, int *y, in stbi_uc *data; FILE *f = fopen(filename, "rb"); if (!f) return NULL; - data = tga_load(x,y,comp,req_comp); + data = stbi_tga_load_from_file(f, x,y,comp,req_comp); fclose(f); return data; } @@ -2818,6 +3066,210 @@ stbi_uc *stbi_tga_load_from_memory (stbi_uc *buffer, int len, int *x, int *y, in return tga_load(x,y,comp,req_comp); } +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int hdr_test(void) +{ + char *signature = "#?RADIANCE\n"; + int i; + for (i=0; signature[i]; ++i) + if (get8() != signature[i]) + return 0; + return 1; +} + +int stbi_hdr_test_memory(stbi_uc *buffer, int len) +{ + start_mem(buffer, len); + return hdr_test(); +} + +#ifndef STBI_NO_STDIO +int stbi_hdr_test_file(FILE *f) +{ + int r,n = ftell(f); + start_file(f); + r = hdr_test(); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +#define HDR_BUFLEN 1024 +static char *hdr_gettoken(char *buffer) +{ + int len=0; + char *s = buffer, c = '\0'; + + c = get8(); + + while (!at_eof() && c != '\n') { + buffer[len++] = c; + if (len == HDR_BUFLEN-1) { + // flush to end of line + while (!at_eof() && get8() != '\n') + ; + break; + } + c = get8(); + } + + buffer[len] = 0; + return buffer; +} + +static void hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 255; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 255; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + + +static float *hdr_load(int *x, int *y, int *comp, int req_comp) +{ + char buffer[HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + + + // Check identifier + if (strcmp(hdr_gettoken(buffer), "#?RADIANCE") != 0) + return ep("not HDR", "Corrupt HDR image"); + + // Parse header + while(1) { + token = hdr_gettoken(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"); + + // 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 += 3; + height = strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return ep("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = strtol(token, NULL, 10); + + *x = width; + *y = height; + + *comp = 3; + if (req_comp == 0) req_comp = 3; + + // Read data + hdr_data = (float *) malloc(height * width * req_comp * sizeof(float)); + + // Load image data + // image data is stored as some number of sca + if( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + getn(rgbe, 4); + hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = get8(); + c2 = get8(); + len = get8(); + 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() }; + hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + free(scanline); + 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"); } + if (scanline == NULL) scanline = (stbi_uc *) malloc(width * 4); + + for (k = 0; k < 4; ++k) { + i = 0; + while (i < width) { + count = get8(); + if (count > 128) { + // Run + value = get8(); + 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(); + } + } + } + for (i=0; i < width; ++i) + hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + free(scanline); + } + + return hdr_data; +} + +#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); +} +#endif + +float *stbi_hdr_load_from_memory(stbi_uc *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + start_mem(buffer, len); + return hdr_load(x,y,comp,req_comp); +} + +#endif // STBI_NO_HDR /////////////////////// write image ///////////////////////