integrate Won Chun's GDI+ support

This commit is contained in:
nothings.org 2007-07-31 01:50:55 +00:00
parent 2bfc0ab8d0
commit dc29e67c21
2 changed files with 289 additions and 44 deletions

309
imv.c
View File

@ -36,9 +36,11 @@
#include "resource.h" #include "resource.h"
typedef int Bool;
// general configuration options // general configuration options
#define USE_GDIPLUS
#define USE_FREEIMAGE #define USE_FREEIMAGE
// size of border in pixels // size of border in pixels
@ -322,7 +324,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 // 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) // windows-compatible bitmap with 4-byte aligned rows and BGR color order)
void make_image(Image *z, int image_x, int image_y, uint8 *image_data, int image_n) 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=0;
z->pixels = image_data; z->pixels = image_data;
@ -334,10 +336,13 @@ void make_image(Image *z, int image_x, int image_y, uint8 *image_data, int image
for (j=0; j < image_y; ++j) { for (j=0; j < image_y; ++j) {
for (i=0; i < image_x; ++i) { for (i=0; i < image_x; ++i) {
// TODO: hoist branches outside of loops?
if (image_loaded_as_rgb) {
// swap RGB to BGR // swap RGB to BGR
unsigned char t = image_data[k+0]; unsigned char t = image_data[k+0];
image_data[k+0] = image_data[k+2]; image_data[k+0] = image_data[k+2];
image_data[k+2] = t; image_data[k+2] = t;
}
#if BPP==4 #if BPP==4
// if image had an alpha channel, pre-blend with background // if image had an alpha channel, pre-blend with background
@ -418,7 +423,7 @@ start:
return best; return best;
} }
static uint8 *imv_decode_from_memory(uint8 *mem, int len, int *x, int *y, 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);
static char *imv_failure_reason(void); static char *imv_failure_reason(void);
void *decode_task(void *p) void *decode_task(void *p)
@ -434,13 +439,13 @@ void *decode_task(void *p)
stb_sem_waitfor(decode_queue); stb_sem_waitfor(decode_queue);
o(("DECODE: woken\n")); o(("DECODE: woken\n"));
} else { } else {
int x,y,n; int x,y,loaded_as_rgb,n;
uint8 *data; uint8 *data;
assert(f->status == LOAD_decoding); assert(f->status == LOAD_decoding);
// decode image // decode image
o(("DECIDE: decoding %s\n", f->filename)); o(("DECIDE: decoding %s\n", f->filename));
data = imv_decode_from_memory(f->filedata, f->len, &x, &y, &n, BPP); data = imv_decode_from_memory(f->filedata, f->len, &x, &y, &loaded_as_rgb, &n, BPP);
o(("DECODE: decoded %s\n", f->filename)); o(("DECODE: decoded %s\n", f->filename));
// free copy of data from disk, which we don't need anymore // free copy of data from disk, which we don't need anymore
@ -457,7 +462,7 @@ void *decode_task(void *p)
} else { } else {
// post-process the image into the right format // post-process the image into the right format
f->image = (Image *) malloc(sizeof(*f->image)); f->image = (Image *) malloc(sizeof(*f->image));
make_image(f->image, x,y,data,n); make_image(f->image, x, y,data, loaded_as_rgb, n);
barrier(); barrier();
f->status = LOAD_available; f->status = LOAD_available;
@ -558,7 +563,7 @@ struct
} *fileinfo; } *fileinfo;
// declare with extra bytes so we can print the version number into it // declare with extra bytes so we can print the version number into it
char helptext_center[140] = char helptext_center[150] =
"imv(stb)\n" "imv(stb)\n"
"Copyright 2007 Sean Barrett\n" "Copyright 2007 Sean Barrett\n"
"http://code.google.com/p/stb-imv\n" "http://code.google.com/p/stb-imv\n"
@ -566,7 +571,7 @@ char helptext_center[140] =
; ;
char helptext_left[] = char helptext_left[] =
"\n\n\n\n\n\n" "\n\n\n\n\n\n\n"
" ALT-ENTER: toggle size\n" " ALT-ENTER: toggle size\n"
" CTRL-PLUS: zoom in\n" " CTRL-PLUS: zoom in\n"
"CTRL-MINUS: zoom out\n" "CTRL-MINUS: zoom out\n"
@ -2298,6 +2303,14 @@ int cur_is_current(void)
return !strcmp(cur_filename, source_c->filename); return !strcmp(cur_filename, source_c->filename);
} }
#ifdef USE_GDIPLUS
static Bool GdiplusPresent;
static Bool LoadGdiplus(void);
#define ICM_SUFFIX ""
// use this definition to enable color management
//#define ICM_SUFFIX "ICM"
#endif
#ifdef USE_FREEIMAGE #ifdef USE_FREEIMAGE
static int FreeImagePresent; static int FreeImagePresent;
static int LoadFreeImage(void); static int LoadFreeImage(void);
@ -2315,21 +2328,11 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
HWND hWnd; HWND hWnd;
// initial loaded image // initial loaded image
int image_x, image_y, image_n; int image_x, image_y, image_loaded_as_rgb, image_n;
unsigned char *image_data; unsigned char *image_data;
inst = hInstance; inst = hInstance;
#ifdef _DEBUG
{
char buffer[1024];
FILE *f = fopen("c:/x/cmdline.txt", "ab");
sprintf(buffer, "%s\n\nx\n", lpCmdLine);
fwrite(buffer, 1, strlen(buffer), f);
fclose(f);
}
#endif
// determine the number of threads to use in the resizer // determine the number of threads to use in the resizer
resize_threads = stb_min(stb_processor_count(), 16); resize_threads = stb_min(stb_processor_count(), 16);
@ -2347,6 +2350,13 @@ fclose(f);
// we can't do this statically with the current build process // we can't do this statically with the current build process
strcat(helptext_center, VERSION); strcat(helptext_center, VERSION);
#ifdef USE_GDIPLUS
if (LoadGdiplus()) {
strcat(helptext_center, "\nUsing GDI+");
open_filter = "Image Files\0*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.ico;*.jng;*.tiff\0";
}
#endif
// now try to LoadFreeImage _after_ we've already loaded the prefs; // now try to LoadFreeImage _after_ we've already loaded the prefs;
// that way only_stbi can be used to suppress errors // that way only_stbi can be used to suppress errors
#ifdef USE_FREEIMAGE #ifdef USE_FREEIMAGE
@ -2408,7 +2418,7 @@ fclose(f);
if (!data) if (!data)
why = "Couldn't open file"; why = "Couldn't open file";
else { else {
image_data = imv_decode_from_memory(data, len, &image_x, &image_y, &image_n, BPP); image_data = imv_decode_from_memory(data, len, &image_x, &image_y, &image_loaded_as_rgb, &image_n, BPP);
if (image_data == NULL) if (image_data == NULL)
why = imv_failure_reason(); why = imv_failure_reason();
} }
@ -2443,7 +2453,7 @@ fclose(f);
// create the source image by converting the image data to BGR, // create the source image by converting the image data to BGR,
// pre-blending alpha // pre-blending alpha
source = malloc(sizeof(*source)); source = malloc(sizeof(*source));
make_image(source, image_x, image_y, image_data, image_n); make_image(source, image_x, image_y, image_data, image_loaded_as_rgb, image_n);
// create a cache entry in case they start browsing later // create a cache entry in case they start browsing later
cache[0].status = LOAD_available; cache[0].status = LOAD_available;
@ -3201,6 +3211,232 @@ static char *imv_failure_reason(void)
return imv_failure_string; return imv_failure_string;
} }
#ifdef USE_GDIPLUS
#pragma pack(push,8)
// from GdiplusTypes.h
typedef enum
{
GpOk = 0,
GpGenericError = 1,
GpInvalidParameter = 2,
GpOutOfMemory = 3,
GpObjectBusy = 4,
GpInsufficientBuffer = 5,
GpNotImplemented = 6,
GpWin32Error = 7,
GpWrongState = 8,
GpAborted = 9,
GpFileNotFound = 10,
GpValueOverflow = 11,
GpAccessDenied = 12,
GpUnknownImageFormat = 13,
GpFontFamilyNotFound = 14,
GpFontStyleNotFound = 15,
GpNotTrueTypeFont = 16,
GpUnsupportedGdiplusVersion = 17,
GpGdiplusNotInitialized = 18,
GpPropertyNotFound = 19,
GpPropertyNotSupported = 20
} GpStatus;
// from GdiplusInit.h
typedef enum
{
DebugEventLevelFatal,
DebugEventLevelWarning
} GpDebugEventLevel;
//typedef void (*MyFunc)(int fif, const char *msg);
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 SuppressExternalCodecs;
} GdiplusStartupInput;
typedef struct {
GpNotificationHookProc NoticationHook;
GpNotificationUnhookProc NotificatoinUnhook;
} GdiplusStartupOutput;
static HINSTANCE GdiplusDLL;
static GdiplusStartupOutput gpStartupOutput;
typedef __declspec(dllimport) GpStatus (WINAPI *GdiplusStartupProc)(ULONG* token, const GdiplusStartupInput* input, GdiplusStartupOutput* output);
static GdiplusStartupProc GdiplusStartup;
// from GdiplusHeaders.h
typedef void GpImage; // opaque type
typedef struct {
GpImage* nativeImage;
GpStatus lastResult;
GpStatus loadStatus;
} GpBitmap;
// from GdiplusTypes.h
typedef struct {
INT X;
INT Y;
INT Width;
INT Height;
} GpRect;
// from GdiplusPixelFormat.h
typedef INT GpPixelFormat;
// In-memory pixel data formats:
// bits 0-7 = format index
// bits 8-15 = pixel size (in bits)
// bits 16-23 = flags
// bits 24-31 = reserved
#define GpPixelFormatGDI 0x00020000 // Is a GDI-supported format
#define GpPixelFormatAlpha 0x00040000 // Has an alpha component
#define GpPixelFormatCanonical 0x00200000
#define GpPixelFormat24bppRGB (8 | (24 << 8) | GpPixelFormatGDI)
#define GpPixelFormat32bppARGB (10 | (32 << 8) | GpPixelFormatAlpha | GpPixelFormatGDI | GpPixelFormatCanonical)
// from GdiplusImaging.h
typedef struct {
UINT Width;
UINT Height;
INT Stride;
GpPixelFormat PixelFormat;
VOID* Scan0;
UINT_PTR Reserved;
} GpBitmapData;
typedef enum
{
GpImageLockModeRead = 0x0001,
GpImageLockModeWrite = 0x0002,
GpImageLockModeUserInputBuf= 0x0004
} GpImageLockMode;
typedef __declspec(dllimport) GpStatus (WINAPI *GdipCreateBitmapFromStreamProc)(IStream* stream, GpBitmap **bitmap);
static GdipCreateBitmapFromStreamProc GdipCreateBitmapFromStream;
typedef __declspec(dllimport) GpStatus (WINAPI *GdipDisposeImageProc)(GpImage *image);
static GdipDisposeImageProc GdipDisposeImage;
typedef __declspec(dllimport) GpStatus (WINAPI *GdipBitmapLockBitsProc)(GpBitmap* bitmap, const GpRect* rect, GpImageLockMode flags, GpPixelFormat format, GpBitmapData* lockedBitmapData);
static GdipBitmapLockBitsProc GdipBitmapLockBits;
typedef __declspec(dllimport) GpStatus (WINAPI *GdipBitmapUnlockBitsProc)(GpBitmap* bitmap, GpBitmapData* lockedBitmapData);
static GdipBitmapUnlockBitsProc GdipBitmapUnlockBits;
FARPROC GpFunc(char *str)
{
FARPROC p = GetProcAddress(GdiplusDLL, str);
if (p == NULL)
GdiplusPresent = FALSE; // if something doesn't load, bail!
return p;
}
static Bool LoadGdiplus(void)
{
static Bool InitializationAttempted = FALSE;
if (InitializationAttempted)
return GdiplusPresent;
InitializationAttempted = TRUE;
GdiplusPresent = FALSE;
GdiplusDLL = LoadLibrary("gdiplus.dll");
if (!GdiplusDLL)
return GdiplusPresent;
GdiplusPresent = TRUE;
GdiplusStartup = (GdiplusStartupProc)GpFunc("GdiplusStartup");
GdipCreateBitmapFromStream = (GdipCreateBitmapFromStreamProc)GpFunc("GdipCreateBitmapFromStream" ICM_SUFFIX);
GdipDisposeImage = (GdipDisposeImageProc)GpFunc("GdipDisposeImage");
GdipBitmapLockBits = (GdipBitmapLockBitsProc)GpFunc("GdipBitmapLockBits");
GdipBitmapUnlockBits = (GdipBitmapUnlockBitsProc)GpFunc("GdipBitmapUnlockBits");
if (!GdiplusPresent) {
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) {
GdiplusPresent = FALSE;
if (!only_stbi)
error("Failed to initialize GdiPlus.dll; disabling GDI+ support.");
}
}
}
return GdiplusPresent;
}
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;
GpBitmapData data;
HGLOBAL hmem;
GpPixelFormat pixelFormat;
size_t i, image_sz;
uint8* buf, *ret = NULL;
image_sz = 0;
*x = 0;
*y = 0;
*n = n_req;
bitmap = NULL;
data.Scan0 = NULL;
hmem = GlobalAlloc(GMEM_MOVEABLE, len);
if (!hmem)
goto liwgExit;
buf = GlobalLock(hmem);
if (!buf)
goto liwgExit;
memcpy(buf, mem, len);
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 (GdipBitmapLockBits(bitmap, NULL, GpImageLockModeRead, pixelFormat, &data) != GpOk)
goto liwgExit;
*x = data.Width;
*y = data.Height;
*n = n_req;
image_sz = data.Width * data.Height * n_req;
ret = (uint8*)malloc(image_sz);
for (i=0; i<data.Height; ++i)
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);
return ret;
}
#pragma pack(pop)
#endif
#ifdef USE_FREEIMAGE #ifdef USE_FREEIMAGE
// FreeImage types // FreeImage types
@ -3326,13 +3562,7 @@ uint8 *LoadImageWithFreeImage(FIMEMORY *fi, int *x, int *y, int *n, int n_req)
Result = (uint8 *) malloc(Width * Height * BPP); Result = (uint8 *) malloc(Width * Height * BPP);
if(Result) { if(Result) {
int i;
FreeImage_ConvertToRawBits(Result, Bitmap, BPP*Width, BPP*8, 0xff0000,0x00ff00,0xff, FALSE); FreeImage_ConvertToRawBits(Result, Bitmap, BPP*Width, BPP*8, 0xff0000,0x00ff00,0xff, FALSE);
for (i=0; i < Width*Height*BPP; i += BPP) {
uint8 t = Result[i];
Result[i] = Result[i+2];
Result[i+2] = t;
}
*x = Width; *x = Width;
*y = Height; *y = Height;
*n = FreeImage_IsTransparent(Bitmap) ? 4 : 3; *n = FreeImage_IsTransparent(Bitmap) ? 4 : 3;
@ -3343,17 +3573,37 @@ uint8 *LoadImageWithFreeImage(FIMEMORY *fi, int *x, int *y, int *n, int n_req)
} }
#endif #endif
static uint8 *imv_decode_from_memory(uint8 *mem, int len, int *x, int *y, 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)
{ {
uint8 *res = NULL; uint8 *res = NULL;
imv_failure_string = NULL; imv_failure_string = NULL;
// prefer STBI over everything else
*loaded_as_rgb = FALSE;
res = stbi_load_from_memory(mem, len, x, y, n, n_req); res = stbi_load_from_memory(mem, len, x, y, n, n_req);
if (res) return res; if (res) {
*loaded_as_rgb = TRUE;
return res;
}
imv_failure_string = stbi_failure_reason(); imv_failure_string = stbi_failure_reason();
if (only_stbi)
return res;
// prefer GDI+ over FreeImage
#ifdef USE_GDIPLUS
if (GdiplusPresent) {
res = LoadImageWithGdiplus(mem, len, x, y, n, n_req);
if (res)
return res;
}
#endif
// FreeImage is the final fallback
#ifdef USE_FREEIMAGE #ifdef USE_FREEIMAGE
if (!only_stbi) {
if (FreeImagePresent) { if (FreeImagePresent) {
FIMEMORY *fi = FreeImage_OpenMemory(mem,len); FIMEMORY *fi = FreeImage_OpenMemory(mem,len);
res = LoadImageWithFreeImage(fi, x, y, n, n_req); res = LoadImageWithFreeImage(fi, x, y, n, n_req);
@ -3361,7 +3611,6 @@ static uint8 *imv_decode_from_memory(uint8 *mem, int len, int *x, int *y, int *n
// if no error message is generated, because it's not a known type, // if no error message is generated, because it's not a known type,
// we'll get the unknown-type message from stbi_failure_reason() // we'll get the unknown-type message from stbi_failure_reason()
} }
}
#endif #endif
return res; return res;
} }

View File

@ -129,10 +129,6 @@ SOURCE=..\imv.ico
# End Source File # End Source File
# Begin Source File # Begin Source File
SOURCE=.\mono_thumb.c
# End Source File
# Begin Source File
SOURCE=..\notes.txt SOURCE=..\notes.txt
!IF "$(CFG)" == "stb_imv - Win32 Release" !IF "$(CFG)" == "stb_imv - Win32 Release"