diff --git a/imv.c b/imv.c index d110605..17aa621 100644 --- a/imv.c +++ b/imv.c @@ -35,6 +35,25 @@ #include "resource.h" + +// general configuration options + +#define USE_FREEIMAGE + +// size of border in pixels +#define FRAME 3 + +// location within frame of secondary border +#define FRAME2 (FRAME >> 1) + +// color of secondary border +#define GREY 192 + + + + + + // all programs get the version number from the same place: version.bat #define set static char * #include "version.bat" @@ -64,16 +83,6 @@ void ods(char *str, ...) #endif -// size of border in pixels -#define FRAME 3 - -// location within frame of secondary border -#define FRAME2 (FRAME >> 1) - -// color of secondary border -#define GREY 192 - - // internal messages (all used for waking up main thread from tasks) enum { @@ -409,6 +418,8 @@ start: return best; } +static uint8 *imv_decode_from_memory(uint8 *mem, int len, int *x, int *y, int *n, int n_req); + void *decode_task(void *p) { for(;;) { @@ -428,7 +439,7 @@ void *decode_task(void *p) // decode image o(("DECIDE: decoding %s\n", f->filename)); - data = stbi_load_from_memory(f->filedata, f->len, &x, &y, &n, BPP); + data = imv_decode_from_memory(f->filedata, f->len, &x, &y, &n, BPP); o(("DECODE: decoded %s\n", f->filename)); // free copy of data from disk, which we don't need anymore @@ -536,9 +547,8 @@ int show_help=0; int downsample_cubic = 0; int upsample_cubic = TRUE; - // declare with extra bytes so we can print the version number into it -char helptext_center[88] = +char helptext_center[104] = "imv(stb)\n" "Copyright 2007 Sean Barrett\n" "http://code.google.com/p/stb-imv\n" @@ -546,7 +556,7 @@ char helptext_center[88] = ; char helptext_left[] = - "\n\n\n\n" + "\n\n\n\n\n" " ESC: exit\n" " ALT-ENTER: toggle size\n" " CTRL-PLUS: zoom in\n" @@ -563,7 +573,7 @@ char helptext_left[] = ; char helptext_right[] = - "\n\n\n\n\n" + "\n\n\n\n\n\n" "right-click to exit\n" "left drag center to move\n" "left drag edges to resize\n" @@ -1096,6 +1106,8 @@ void free_fileinfo(void) fileinfo = NULL; } +char *open_filter = "Image Files\0*.jpg;*.jpeg;*.png;*.bmp\0"; + // build a filelist for the current directory void init_filelist(void) { @@ -1109,7 +1121,7 @@ void init_filelist(void) free_fileinfo(); } - image_files = stb_readdir_files_mask(path_to_file, "*.jpg;*.jpeg;*.png;*.bmp"); + image_files = stb_readdir_files_mask(path_to_file, open_filter + 12); if (image_files == NULL) { error("Error: couldn't read directory."); exit(0); } // given the array of filenames, build an equivalent fileinfo array @@ -1365,12 +1377,13 @@ void advance(int dir) // the filelist, and force it to load (and prefetch) // with 'advance' static char filenamebuffer[4096]; + void open_file(void) { OPENFILENAME o; memset(&o, 0, sizeof(o)); o.lStructSize = sizeof(o); - o.lpstrFilter = "Image Files\0*.jpg;*.jpeg;*.png;*.bmp\0"; + o.lpstrFilter = open_filter; o.lpstrFile = filenamebuffer; filenamebuffer[0] = 0; o.nMaxFile = sizeof(filenamebuffer); @@ -1639,6 +1652,8 @@ int reg_set(char *str, void *data, int len) return (ERROR_SUCCESS == RegSetValueEx(zreg, str, 0, REG_BINARY, data, len)); } +int only_stbi=FALSE; + // we use very short strings for these to avoid wasting space, since // people shouldn't be mucking with them directly anyway! void reg_save(void) @@ -1651,6 +1666,7 @@ void reg_save(void) reg_set("cache", &temp, 4); reg_set("lfs", &label_font_height, 4); reg_set("label", &show_label, 4); + reg_set("stbi", &only_stbi, 4); RegCloseKey(zreg); } } @@ -1666,6 +1682,7 @@ void reg_load(void) reg_get("label", &show_label, 4); if (reg_get("cache", &temp, 4)) max_cache_bytes = temp << 20; + reg_get("stbi", &only_stbi, 4); RegCloseKey(zreg); } } @@ -1730,6 +1747,7 @@ BOOL CALLBACK PrefDlgProc(HWND hdlg, UINT imsg, WPARAM wparam, LPARAM lparam) // copy preferences into dialog SendMessage(GetDlgItem(hdlg, DIALOG_upsample), BM_SETCHECK, upsample_cubic, 0); SendMessage(GetDlgItem(hdlg, DIALOG_showlabel), BM_SETCHECK, show_label, 0); + SendMessage(GetDlgItem(hdlg, DIALOG_stbi_only), BM_SETCHECK, only_stbi, 0); for (i=0; i < 6; ++i) set_dialog_number(DIALOG_r1+i, alpha_background[0][i]); set_dialog_number(DIALOG_cachesize, max_cache_bytes >> 20); @@ -1780,6 +1798,7 @@ BOOL CALLBACK PrefDlgProc(HWND hdlg, UINT imsg, WPARAM wparam, LPARAM lparam) label_font_height = get_dialog_number(DIALOG_labelheight); upsample_cubic = BST_CHECKED == SendMessage(GetDlgItem(hdlg,DIALOG_upsample ), BM_GETCHECK,0,0); show_label = BST_CHECKED == SendMessage(GetDlgItem(hdlg,DIALOG_showlabel), BM_GETCHECK,0,0); + only_stbi = BST_CHECKED == SendMessage(GetDlgItem(hdlg,DIALOG_stbi_only), BM_GETCHECK,0,0); // if alpha_background changed, clear the cache of any images that used it if (memcmp(alpha_background, cur, 6)) { @@ -1828,6 +1847,33 @@ BOOL CALLBACK PrefDlgProc(HWND hdlg, UINT imsg, WPARAM wparam, LPARAM lparam) return FALSE; } +#ifdef PERFTEST +void performance_test(void) +{ + int t1,t2; + int len,i; + 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); + free(result); + } + + t2 = timeGetTime(); + free(buffer); + + { + char buffer[512]; + sprintf(buffer, "Decode time: %f ms\n", (t2-t1)/50.0); + error(buffer); + } +} +#endif + // missing VK definitions in old compiler #ifndef VK_OEM_PLUS #define VK_OEM_PLUS 0xbb @@ -2017,6 +2063,11 @@ int WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) extra_border = show_frame; if (cur) frame(cur); break; +#ifdef PERFTEST + case 'D' | MY_CTRL: + performance_test(); + break; +#endif case 'P': case 'P' | MY_CTRL: @@ -2066,6 +2117,11 @@ int cur_is_current(void) return !strcmp(cur_filename, source_c->filename); } +#ifdef USE_FREEIMAGE +static int FreeImagePresent; +static int LoadFreeImage(void); +#endif + int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { int argc; @@ -2083,10 +2139,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine inst = hInstance; - // concatenate the version number onto the help text, because - // we can't do this statically with the current build process - strcat(helptext_center, VERSION); - // determine the number of threads to use in the resizer resize_threads = stb_min(stb_processor_count(), 16); resize_threads = 8; @@ -2101,6 +2153,20 @@ resize_threads = 8; // load the registry preferences, if they're there (AFTER the above) reg_load(); + // concatenate the version number onto the help text, because + // we can't do this statically with the current build process + strcat(helptext_center, VERSION); + + // now try to LoadFreeImage _after_ we've already loaded the prefs; + // that way only_stbi can be used to suppress errors +#ifdef USE_FREEIMAGE + if (LoadFreeImage()) { + strcat(helptext_center, "\nUsing FreeImage.dll"); + open_filter = "Image Files\0*.jpg;*.jpeg;*.png;*.bmp;*.dds;*.gif;*.ico;*.jng;*.lbm;*.pcx;*.ppm;*.psd;*.tga;*.tiff\0"; + } +#endif + assert(helptext_center[sizeof(helptext_center)-1]==0); + // create the main window class memset(&wndclass, 0, sizeof(wndclass)); wndclass.cbSize = sizeof(wndclass); @@ -2132,7 +2198,7 @@ resize_threads = 8; OPENFILENAME o; memset(&o, 0, sizeof(o)); o.lStructSize = sizeof(o); - o.lpstrFilter = "Image Files\0*.jpg;*.jpeg;*.png;*.bmp\0"; + o.lpstrFilter = open_filter; o.lpstrFile = filenamebuffer; filenamebuffer[0] = 0; o.nMaxFile = sizeof(filenamebuffer); @@ -2156,7 +2222,7 @@ resize_threads = 8; if (!data) why = "Couldn't open file"; else { - image_data = stbi_load_from_memory(data, len, &image_x, &image_y, &image_n, BPP); + image_data = imv_decode_from_memory(data, len, &image_x, &image_y, &image_n, BPP); if (image_data == NULL) why = stbi_failure_reason(); } @@ -2935,3 +3001,172 @@ void image_resize(Image *dest, Image *src) } #endif } + + + +#ifdef USE_FREEIMAGE + +// FreeImage types + +typedef int FREE_IMAGE_FORMAT; +typedef struct FIBITMAP FIBITMAP; +typedef struct FIMEMORY FIMEMORY; + +typedef void (*FreeImage_OutputMessageFunction)(FREE_IMAGE_FORMAT fif, const char *msg); + +#define fitype(x) typedef __declspec(dllimport) x __stdcall + + +// FreeImage functions that we're going to import; +fitype(void) freeimage_setoutputmessage(FreeImage_OutputMessageFunction omf); +static freeimage_setoutputmessage *FreeImage_SetOutputMessage; + +fitype(FIMEMORY *) freeimage_openmemory(uint8 *data, unsigned size); +static freeimage_openmemory *FreeImage_OpenMemory; + +fitype(void) freeimage_closememory(FIMEMORY *stream); +static freeimage_closememory *FreeImage_CloseMemory; + +fitype(int) freeimage_seekmemory(FIMEMORY *stream, long offset, int origin); +static freeimage_seekmemory *FreeImage_SeekMemory; + +fitype(FIBITMAP *) freeimage_loadfrommemory(FREE_IMAGE_FORMAT fif, FIMEMORY *fi, int flags); +static freeimage_loadfrommemory *FreeImage_LoadFromMemory; + +fitype(void) freeimage_converttorawbits(BYTE *bits, FIBITMAP *dib, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown); +static freeimage_converttorawbits *FreeImage_ConvertToRawBits; + +typedef __declspec(dllimport) void __stdcall freeimage_unload(FIBITMAP *dib); +static freeimage_unload *FreeImage_Unload; + +typedef __declspec(dllimport) unsigned __stdcall freeimage_getwidth(FIBITMAP *dib); +static freeimage_getwidth *FreeImage_GetWidth; + +typedef __declspec(dllimport) unsigned __stdcall freeimage_getheight(FIBITMAP *dib); +static freeimage_getheight *FreeImage_GetHeight; + +typedef __declspec(dllimport) FREE_IMAGE_FORMAT __stdcall freeimage_getfiletypefrommemory(FIMEMORY *fi, int size); +static freeimage_getfiletypefrommemory *FreeImage_GetFileTypeFromMemory; + +typedef __declspec(dllimport) FREE_IMAGE_FORMAT __stdcall freeimage_getfiffromfilename(const char *filename); +static freeimage_getfiffromfilename *FreeImage_GetFIFFromFilename; + +typedef __declspec(dllimport) BYTE *__stdcall freeimage_getbits(FIBITMAP *dib); +static freeimage_getbits *FreeImage_GetBits; + +typedef __declspec(dllimport) int __stdcall freeimage_istransparent(FIBITMAP *dib); +static freeimage_istransparent *FreeImage_IsTransparent; + +static void +FreeImageErrorHandler(FREE_IMAGE_FORMAT fif, const char *message) +{ + assert(!"FreeImage failure"); +} + +static HINSTANCE FreeImageDLL; + +FARPROC fifunc(char *str) +{ + FARPROC p = GetProcAddress(FreeImageDLL, str); + if (p == NULL) + FreeImagePresent = FALSE; // if something doesn't load, bail! + return p; +} + +static int LoadFreeImage(void) +{ + static int InitializationAttempted; + if(!InitializationAttempted) + { + FreeImageDLL = LoadLibrary("FreeImage.dll"); + if(FreeImageDLL) + { + FreeImagePresent = TRUE; + FreeImage_ConvertToRawBits = (freeimage_converttorawbits *)fifunc("_FreeImage_ConvertToRawBits@32"); + FreeImage_GetBits = (freeimage_getbits *)fifunc("_FreeImage_GetBits@4"); + FreeImage_GetFIFFromFilename = (freeimage_getfiffromfilename *)fifunc("_FreeImage_GetFIFFromFilename@4"); + FreeImage_GetFileTypeFromMemory = (freeimage_getfiletypefrommemory *)fifunc("_FreeImage_GetFileTypeFromMemory@8"); + FreeImage_GetHeight = (freeimage_getheight *)fifunc("_FreeImage_GetHeight@4"); + FreeImage_GetWidth = (freeimage_getwidth *)fifunc("_FreeImage_GetWidth@4"); + FreeImage_LoadFromMemory = (freeimage_loadfrommemory *)fifunc("_FreeImage_LoadFromMemory@12"); + FreeImage_SetOutputMessage = (freeimage_setoutputmessage *)fifunc("_FreeImage_SetOutputMessage@4"); + FreeImage_Unload = (freeimage_unload *)fifunc("_FreeImage_Unload@4"); + FreeImage_OpenMemory = (freeimage_openmemory *)fifunc("_FreeImage_OpenMemory@8"); + FreeImage_CloseMemory = (freeimage_closememory *)fifunc("_FreeImage_CloseMemory@4"); + FreeImage_SeekMemory = (freeimage_seekmemory *)fifunc("_FreeImage_SeekMemory@12"); + FreeImage_IsTransparent = (freeimage_istransparent *)fifunc("_FreeImage_IsTransparent@4"); + + if (!FreeImagePresent) { + if (!only_stbi) + error("Invalid FreeImage.dll; disabling FreeImage support."); + } else + FreeImage_SetOutputMessage(FreeImageErrorHandler); + } + + InitializationAttempted = TRUE; + } + + return(FreeImagePresent); +} + +uint8 *LoadImageWithFreeImage(FIMEMORY *fi, int *x, int *y, int *n, int n_req) +{ + uint8 *Result = 0; + FREE_IMAGE_FORMAT FileFormat = FreeImage_GetFileTypeFromMemory(fi, 0); + FIBITMAP *Bitmap; + if(FileFormat == -1) + { + // @TODO: propogate the filename to here + // bail! + return NULL; + // FileFormat = FreeImage_GetFIFFromFilename(FromFilename); + } + + FreeImage_SeekMemory(fi, 0, SEEK_SET); + + Bitmap = FreeImage_LoadFromMemory(FileFormat, fi, 0); + if(Bitmap) + { + int32 Width = FreeImage_GetWidth(Bitmap); + int32 Height = FreeImage_GetHeight(Bitmap); + + Result = (uint8 *) malloc(Width * Height * BPP); + if(Result) + { + int i; + FreeImage_ConvertToRawBits(Result, Bitmap, BPP*Width, BPP*8, 0xff0000,0x00ff00,0xff, FALSE); +#ifndef PERFTEST + for (i=0; i < Width*Height*BPP; i += BPP) { + uint8 t = Result[i]; + Result[i] = Result[i+2]; + Result[i+2] = t; + } +#endif + *x = Width; + *y = Height; + *n = FreeImage_IsTransparent(Bitmap) ? 4 : 3; + } + + FreeImage_Unload(Bitmap); + } + + return(Result); +} +#endif + +static uint8 *imv_decode_from_memory(uint8 *mem, int len, int *x, int *y, int *n, int n_req) +{ + uint8 *res = NULL; +#ifdef USE_FREEIMAGE + if (!only_stbi) { + if (res == NULL && FreeImagePresent) { + FIMEMORY *fi = FreeImage_OpenMemory(mem,len); + res = LoadImageWithFreeImage(fi, x, y, n, n_req); + FreeImage_CloseMemory(fi); + } + if (res) return res; + } +#endif + res = stbi_load_from_memory(mem, len, x, y, n, n_req); + return res; +} diff --git a/pref.rc b/pref.rc index b0f4389..6a6d722 100644 --- a/pref.rc +++ b/pref.rc @@ -52,13 +52,13 @@ END // Dialog // -IDD_pref DIALOG DISCARDABLE 0, 0, 161, 171 +IDD_pref DIALOG DISCARDABLE 0, 0, 161, 196 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "imv(stb) preferences" FONT 8, "MS Sans Serif" BEGIN - DEFPUSHBUTTON "OK",IDOK,23,149,50,14 - PUSHBUTTON "Cancel",IDCANCEL,83,149,50,14 + DEFPUSHBUTTON "OK",IDOK,23,172,50,14 + PUSHBUTTON "Cancel",IDCANCEL,83,172,50,14 CONTROL "High-quality resizing",DIALOG_upsample,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,23,116,76,10 EDITTEXT DIALOG_r1,57,50,19,12,ES_RIGHT | ES_AUTOHSCROLL @@ -79,6 +79,8 @@ BEGIN LTEXT "Label font height",IDC_STATIC,35,101,104,10 CONTROL "Show filename label",DIALOG_showlabel,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,23,87,120,10 + CONTROL "Only use stb_image loaders",DIALOG_stbi_only,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,23,149,111,10 END @@ -95,7 +97,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 154 TOPMARGIN, 7 - BOTTOMMARGIN, 164 + BOTTOMMARGIN, 189 END END #endif // APSTUDIO_INVOKED