From 3b167d592cac5234f18e894e45ed12758fc3ed0b Mon Sep 17 00:00:00 2001 From: "nothings.org" Date: Tue, 14 Aug 2007 17:39:54 +0000 Subject: [PATCH] stb_image fixes; gdi+ fixes merged from Won --- imv.c | 95 +++++++++++++++++++++++++++++++++++------------------ stb_image.c | 23 ++++++++++--- version.bat | 2 +- 3 files changed, 82 insertions(+), 38 deletions(-) diff --git a/imv.c b/imv.c index 18d4de5..c7eea24 100644 --- a/imv.c +++ b/imv.c @@ -231,6 +231,26 @@ 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) { @@ -1972,7 +1992,7 @@ void performance_test(void) { int t1,t2; int len,i; - uint8 *buffer = stb_file(cur_filename, &len); + uint8 *buffer = stb_file2(cur_filename, &len); if (buffer == NULL) return; t1 = timeGetTime(); @@ -2304,7 +2324,10 @@ int cur_is_current(void) } #ifdef USE_GDIPLUS +typedef ULONG ULONG_PTR; + static Bool GdiplusPresent; +static ULONG_PTR GpToken; static Bool LoadGdiplus(void); #define ICM_SUFFIX "" // use this definition to enable color management @@ -2414,7 +2437,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine { char *why=NULL; int len; - uint8 *data = stb_file(filename, &len); + uint8 *data = stb_file2(filename, &len); if (!data) why = "Couldn't open file"; else { @@ -3213,6 +3236,7 @@ static char *imv_failure_reason(void) #ifdef USE_GDIPLUS + #pragma pack(push,8) // from GdiplusTypes.h @@ -3246,20 +3270,19 @@ typedef enum typedef enum { - DebugEventLevelFatal, - DebugEventLevelWarning + GpDebugEventLevelFatal, + GpDebugEventLevelWarning } GpDebugEventLevel; typedef VOID (WINAPI *GpDebugEventProc)(GpDebugEventLevel level, CHAR *message); -typedef ULONG ULONG_PTR; typedef GpStatus (WINAPI *GpNotificationHookProc)(ULONG_PTR *token); typedef VOID (WINAPI *GpNotificationUnhookProc)(ULONG_PTR token); typedef struct { UINT32 GdiplusVersion; GpDebugEventProc DebugEventCallback; - BOOL SuppressBackgroundThread; // TODO: manually call hook/unhook + BOOL SuppressBackgroundThread; BOOL SuppressExternalCodecs; } GdiplusStartupInput; @@ -3271,9 +3294,6 @@ typedef struct { static HINSTANCE GdiplusDLL; static GdiplusStartupOutput gpStartupOutput; -typedef __declspec(dllimport) GpStatus (WINAPI *GdiplusStartupProc)(ULONG_PTR *token, const GdiplusStartupInput* input, GdiplusStartupOutput* output); -static GdiplusStartupProc GdiplusStartup; - // from GdiplusHeaders.h typedef void GpImage; // opaque type @@ -3321,6 +3341,12 @@ typedef enum GpImageLockModeUserInputBuf= 0x0004 } GpImageLockMode; +typedef __declspec(dllimport) GpStatus (WINAPI *GdiplusStartupProc)(ULONG_PTR *token, const GdiplusStartupInput* input, GdiplusStartupOutput* output); +static GdiplusStartupProc GdiplusStartup; + +typedef __declspec(dllimport) GpStatus (WINAPI *GdiplusShutdownProc)(ULONG_PTR token); +static GdiplusShutdownProc GdiplusShutdown; + typedef __declspec(dllimport) GpStatus (WINAPI *GdipCreateBitmapFromStreamProc)(IStream* stream, GpBitmap **bitmap); static GdipCreateBitmapFromStreamProc GdipCreateBitmapFromStream; @@ -3333,6 +3359,8 @@ static GdipBitmapLockBitsProc GdipBitmapLockBits; typedef __declspec(dllimport) GpStatus (WINAPI *GdipBitmapUnlockBitsProc)(GpBitmap* bitmap, GpBitmapData* lockedBitmapData); static GdipBitmapUnlockBitsProc GdipBitmapUnlockBits; +#pragma pack(pop) + FARPROC GpFunc(char *str) { FARPROC p = GetProcAddress(GdiplusDLL, str); @@ -3355,6 +3383,7 @@ static Bool LoadGdiplus(void) GdiplusPresent = TRUE; GdiplusStartup = (GdiplusStartupProc)GpFunc("GdiplusStartup"); + //GdiplusShutdown = (GdiplusShutdownProc)GpFunc("GdiplusShutdown"); GdipCreateBitmapFromStream = (GdipCreateBitmapFromStreamProc)GpFunc("GdipCreateBitmapFromStream" ICM_SUFFIX); GdipDisposeImage = (GdipDisposeImageProc)GpFunc("GdipDisposeImage"); GdipBitmapLockBits = (GdipBitmapLockBitsProc)GpFunc("GdipBitmapLockBits"); @@ -3363,9 +3392,9 @@ static Bool LoadGdiplus(void) if (!only_stbi) error("Invalid GdiPlus.dll; disabling GDI+ support."); } else { - ULONG token; - GdiplusStartupInput gpStartupInput = { 1, NULL, FALSE, FALSE }; - if (GdiplusStartup(&token, &gpStartupInput, &gpStartupOutput) != GpOk) { + // no need to use GDI+ backup thread, or to call the hook methods in gpStartupOutput + GdiplusStartupInput gpStartupInput = { 1, NULL, TRUE, FALSE }; + if (GdiplusStartup(&GpToken, &gpStartupInput, &gpStartupOutput) != GpOk) { GdiplusPresent = FALSE; if (!only_stbi) error("Failed to initialize GdiPlus.dll; disabling GDI+ support."); @@ -3376,39 +3405,43 @@ static Bool LoadGdiplus(void) } static uint8 *LoadImageWithGdiplus(uint8 *mem, int len, int *x, int *y, int *n, int n_req) { - // TODO: implement an IStream that does this in-place, to avoid the copy and allocations - IStream* stream; - GpBitmap* bitmap; + HGLOBAL hMem = NULL; + IStream* stream = NULL; + GpBitmap* bitmap = NULL; GpBitmapData data; - HGLOBAL hmem; GpPixelFormat pixelFormat; - size_t i, image_sz; - uint8* buf, *ret = NULL; + size_t i, image_sz = 0; + uint8 *buf = NULL, *ret = NULL; image_sz = 0; *x = 0; *y = 0; *n = n_req; - bitmap = NULL; data.Scan0 = NULL; - hmem = GlobalAlloc(GMEM_MOVEABLE, len); - if (!hmem) + hMem = GlobalAlloc(GMEM_MOVEABLE, len); + if (!hMem) goto liwgExit; - buf = GlobalLock(hmem); - if (!buf) - goto liwgExit; + buf = GlobalLock(hMem); + if (!buf) goto liwgExit; + memcpy(buf, mem, len); + if (GlobalUnlock(hMem)) + goto liwgExit; + if (CreateStreamOnHGlobal(buf, FALSE, &stream) != S_OK) goto liwgExit; if (GdipCreateBitmapFromStream(stream, &bitmap) != GpOk) goto liwgExit; - if (n_req == 3) - pixelFormat = GpPixelFormat24bppRGB; - else - pixelFormat = GpPixelFormat32bppARGB; +#if BPP == 3 + assert(n_req == 3); + pixelFormat = GpPixelFormat24bppRGB; +#else + assert(n_req == 4); + pixelFormat = GpPixelFormat32bppARGB; +#endif if (GdipBitmapLockBits(bitmap, NULL, GpImageLockModeRead, pixelFormat, &data) != GpOk) goto liwgExit; @@ -3422,16 +3455,14 @@ static uint8 *LoadImageWithGdiplus(uint8 *mem, int len, int *x, int *y, int *n, memcpy(&ret[i*data.Width*n_req], &((uint8*)data.Scan0)[i*data.Stride], data.Width*n_req); liwgExit: - if (buf) GlobalUnlock(hmem); - if (hmem) GlobalFree(hmem); if (data.Scan0) GdipBitmapUnlockBits(bitmap, &data); if (bitmap) GdipDisposeImage(bitmap); + if (stream) stream->lpVtbl->Release(stream); + if (hMem) GlobalFree(hMem); return ret; } -#pragma pack(pop) - #endif #ifdef USE_FREEIMAGE diff --git a/stb_image.c b/stb_image.c index 22bf1d3..cdb296f 100644 --- a/stb_image.c +++ b/stb_image.c @@ -1,4 +1,4 @@ -/* stbi-0.95 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c +/* stbi-0.97 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c when you control the images you're loading QUICK NOTES: @@ -16,6 +16,8 @@ PSD loader history: + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum 0.95 during header scan, seek to markers in case of padding 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same 0.93 handle jpegtran output; verbose errors @@ -1029,12 +1031,14 @@ static int process_frame_header(int scan) 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].h || img_comp[i].h > 4) return e("bad V","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"); } if (scan != SCAN_load) return 1; + if ((1 << 30) / img_x / img_n < 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; @@ -1059,6 +1063,11 @@ static int process_frame_header(int scan) 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); + return e("outofmem", "Out of memory"); + } } return 1; @@ -1270,6 +1279,10 @@ static uint8 *load_jpeg_image(int *out_x, int *out_y, int *comp, int req_comp) // 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); @@ -1965,8 +1978,8 @@ 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","Corrupt PNG"); - img_y = get32(); if (img_y > (1 << 24)) return e("too large","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"); if (color == 3) pal_img_n = 3; else if (color & 1) return e("bad ctype","Corrupt PNG"); @@ -1976,7 +1989,7 @@ static int parse_png_file(int scan, int req_comp) if (!img_x || !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", "Corrupt PNG"); + if ((1 << 30) / img_x / img_n < 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 diff --git a/version.bat b/version.bat index f9049fc..ee0846f 100644 --- a/version.bat +++ b/version.bat @@ -1 +1 @@ -set VERSION="0.94" +set VERSION="0.95"