From 73870f0e8e702f6cf44b349f787686c47c1fff36 Mon Sep 17 00:00:00 2001 From: joostverburg Date: Fri, 12 Sep 2003 14:01:29 +0000 Subject: [PATCH] 1. Supports any bpp for transparent images 2. Windows XP at bpp < 32 possible bug fixed 3. Removed transparency by pallete color index (always use RGB value) 4. shrinked a bit (0.5 kb) git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@2903 212acab6-be3b-0410-9dea-997c60f758d6 --- Contrib/AdvSplash/Example.nsi | 23 +- Contrib/AdvSplash/advsplash.c | 512 ++++++++++++++++---------------- Contrib/AdvSplash/advsplash.txt | 22 +- Plugins/advsplash.dll | Bin 6144 -> 5120 bytes 4 files changed, 272 insertions(+), 285 deletions(-) diff --git a/Contrib/AdvSplash/Example.nsi b/Contrib/AdvSplash/Example.nsi index 3337ed17..86603705 100644 --- a/Contrib/AdvSplash/Example.nsi +++ b/Contrib/AdvSplash/Example.nsi @@ -7,25 +7,28 @@ XPStyle on Function .onInit # the plugins dir is automatically deleted when the installer exits InitPluginsDir - File /oname=$PLUGINSDIR\splash.bmp "${NSISDIR}\Contrib\Makensisw\logo.bmp" + File /oname=$PLUGINSDIR\splash.bmp "${NSISDIR}\Contrib\Icons\modern-header.bmp" #optional #File /oname=$PLUGINSDIR\splash.wav "C:\myprog\sound.wav" + MessageBox MB_OK "Fading" + advsplash::show 1000 600 400 -1 $PLUGINSDIR\splash - Pop $0 ; $0 has '1' if the user closed the splash screen early, + Pop $0 ; $0 has '1' if the user closed the splash screen early, ; '0' if everything closed normal, and '-1' if some error occured. - MessageBox MB_OK "Now with transparency" + MessageBox MB_OK "Transparency" + File /oname=$PLUGINSDIR\splash.bmp "${NSISDIR}\Contrib\Makensisw\logo.bmp" + advsplash::show 2000 0 0 0x1856B1 $PLUGINSDIR\splash + Pop $0 - File /oname=$PLUGINSDIR\splash.bmp "${NSISDIR}\Contrib\Icons\modern.bmp" - - advsplash::show 1000 600 400 0xFF00FF $PLUGINSDIR\splash - - Pop $0 ; $0 has '1' if the user closed the splash screen early, - ; '0' if everything closed normal, and '-1' if some error occured. - MessageBox MB_OK "$0" + MessageBox MB_OK "Transparency/Fading" + File /oname=$PLUGINSDIR\splash.bmp "${NSISDIR}\Contrib\Icons\modern-wizard llama.bmp" + advsplash::show 1000 600 400 0x00005B $PLUGINSDIR\splash + Pop $0 + Delete $PLUGINSDIR\splash.bmp FunctionEnd Section diff --git a/Contrib/AdvSplash/advsplash.c b/Contrib/AdvSplash/advsplash.c index 47539e12..c7abc309 100644 --- a/Contrib/AdvSplash/advsplash.c +++ b/Contrib/AdvSplash/advsplash.c @@ -2,80 +2,284 @@ #define _WIN32_WINNT 0x0500 #include -#include "../exdll/exdll.h" +#include +#include "..\exdll\exdll.h" HINSTANCE g_hInstance; -#define RESOLUTION 20 // 50 fps ;) -#define KEYCOLOR_8BIT_RGBSUPPORT 1 // Includes (1) code for - // specifing key color for 8bit images as RGB +#define RESOLUTION 32 // 30 fps ;) (32? I like SHR more than iDIV ;) BITMAP bm; HBITMAP g_hbm; -int g_rv=-1; -int resolution = RESOLUTION; +int g_rv; +int resolution; int sleep_val, fadein_val, fadeout_val, state, timeleft, keycolor, nt50, alphaparam; -int call = -1; +const char classname[4]="_sp"; typedef BOOL (_stdcall *_tSetLayeredWindowAttributesProc)(HWND hwnd, // handle to the layered window COLORREF crKey, // specifies the color key BYTE bAlpha, // value for the blend function DWORD dwFlags // action ); - - _tSetLayeredWindowAttributesProc SetLayeredWindowAttributesProc; static LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - if (uMsg == WM_CREATE) - { - RECT vp; - - SystemParametersInfo(SPI_GETWORKAREA, 0, &vp, 0); - SetWindowLong(hwnd,GWL_STYLE,0); - SetWindowPos(hwnd,NULL, - vp.left+(vp.right-vp.left-bm.bmWidth)/2, - vp.top+(vp.bottom-vp.top-bm.bmHeight)/2, - bm.bmWidth,bm.bmHeight, - SWP_NOZORDER); - ShowWindow(hwnd,SW_SHOW); - - return 0; - } - if (uMsg == WM_PAINT) - { PAINTSTRUCT ps; RECT r; - - HDC curdc=BeginPaint(hwnd,&ps); - HDC hdc=CreateCompatibleDC(curdc); + HDC curdc = NULL; + HDC hdc; HBITMAP oldbm; - oldbm=(HBITMAP)SelectObject(hdc,g_hbm); - GetClientRect(hwnd,&r); - BitBlt(curdc,r.left,r.top,r.right-r.left,r.bottom-r.top,hdc,0,0,SRCCOPY); + switch (uMsg) + { + case WM_CREATE: + SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0); + SetWindowLong(hwnd,GWL_STYLE,0); + SetWindowPos(hwnd,NULL, + r.left+(r.right-r.left-bm.bmWidth)/2, + r.top+(r.bottom-r.top-bm.bmHeight)/2, + bm.bmWidth,bm.bmHeight, + SWP_NOZORDER | SWP_SHOWWINDOW); + return 0; + + case WM_PAINT: + curdc=BeginPaint(hwnd,&ps); + hdc=CreateCompatibleDC(curdc); + GetClientRect(hwnd,&r); + + oldbm = SelectObject(hdc, g_hbm); + BitBlt(curdc,r.left,r.top,r.right-r.left,r.bottom-r.top,hdc,0,0, SRCCOPY); - SelectObject(hdc,oldbm); - DeleteDC(hdc); - EndPaint(hwnd,&ps); - return 0; - } - if (uMsg == WM_CLOSE) return 0; - if (uMsg == WM_TIMER || uMsg == WM_LBUTTONDOWN) - { - g_rv=(uMsg == WM_LBUTTONDOWN); - DestroyWindow(hwnd); - } - return DefWindowProc(hwnd,uMsg,wParam,lParam); + SelectObject(hdc,oldbm); + DeleteDC(hdc); + EndPaint(hwnd,&ps); + + case WM_CLOSE: + return 0; + + case WM_TIMER: + case WM_LBUTTONDOWN: + g_rv=(uMsg == WM_LBUTTONDOWN); + DestroyWindow(hwnd); + break; + } + return DefWindowProc(hwnd,uMsg,wParam,lParam); +} + +void SetTransparentRegion(HWND myWnd) +{ + HDC dc; + int x, y; + HRGN region, cutrgn; + BITMAPINFO bmi; + int size = bm.bmWidth * bm.bmHeight*4; + int *bmp = GlobalAlloc(GPTR, size); + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biHeight = bm.bmHeight; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = bm.bmWidth; + bmi.bmiHeader.biClrUsed = 0; + bmi.bmiHeader.biClrImportant = 0; + + dc = CreateCompatibleDC(NULL); + SelectObject(dc, g_hbm); + + x = GetDIBits(dc, g_hbm, 0, bm.bmHeight, bmp, &bmi, DIB_RGB_COLORS); + + region = CreateRectRgn(0,0,bm.bmWidth,bm.bmHeight); + + // Search for transparent pixels + for (y = bm.bmHeight-1; y >= 0; y--) + for (x = 0; x < bm.bmWidth; ) + if ((*bmp & 0xFFFFFF) == keycolor) + { + int j = x; + while ((x < bm.bmWidth) && ((*bmp & 0xFFFFFF) == keycolor)) bmp++, x++; + + // Cut transparent pixels from the original region + cutrgn = CreateRectRgn(j, y, x, y+1); + CombineRgn(region, region, cutrgn, RGN_XOR); + DeleteObject(cutrgn); + } else bmp++, x++; + + // Set resulting region. + SetWindowRgn(myWnd, region, TRUE); + DeleteObject(region); + DeleteObject(dc); + GlobalFree(bmp); } BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) { - g_hInstance=hInst; - return TRUE; + g_hInstance=hInst; + return TRUE; } +void CALLBACK TimeProc( + UINT uID, + UINT uMsg, + DWORD dwUser, + DWORD dw1, + DWORD dw2) +{ + int call = -1; + switch (state) + { + // FadeIN + case 0: if (timeleft == 0) + { + timeleft = sleep_val; + state++; + if (nt50) call = 255; + } else { call = ((fadein_val-timeleft)*255)/fadein_val; break; } + // Sleep + case 1: if (timeleft == 0) + { + timeleft = fadeout_val; + state++; + // fadeout + } else break; + // FadeOUT + case 2: if (timeleft == 0) + { + PostMessage((HWND)dwUser, WM_TIMER, 0, 0); + return; + } else { call = ((timeleft)*255)/fadeout_val; break; } + } + // Transparency value aquired, and could be set... + if ((call >= 0) && nt50) + SetLayeredWindowAttributesProc((HWND)dwUser, keycolor, + call, + alphaparam); + + // Time is running out... + timeleft--; +} + +int myatoi(char *s); + +void __declspec(dllexport) show(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) +{ + DEVMODE dm; + char fn[MAX_PATH]; + char temp[64]; + + g_rv = -1; + resolution = RESOLUTION; + + EXDLL_INIT(); + + popstring(temp); + sleep_val = myatoi(temp); + popstring(temp); + fadein_val = myatoi(temp); + popstring(temp); + fadeout_val = myatoi(temp); + popstring(temp); + keycolor = myatoi(temp); + popstring(fn); + + dm.dmSize = sizeof(DEVMODE); + EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm); + // Check for winXP/2k at 32 bpp transparency + nt50 = (LOBYTE(LOWORD(GetVersion())) >= 5) && !((dm.dmBitsPerPel < 32) && (keycolor != -1)); + if (!nt50) + { + // Fading+transparency is unsupported at old windows versions... + resolution = sleep_val + fadein_val + fadeout_val; + fadeout_val = fadein_val = 0; + sleep_val = 1; + } else + { + // div them by resolution + sleep_val >>= 5; + fadein_val >>= 5; + fadeout_val >>= 5; + + alphaparam = LWA_ALPHA | ((keycolor == -1)?(0):(LWA_COLORKEY)); + keycolor = ((keycolor & 0xFF) << 16) + (keycolor & 0xFF00) + ((keycolor & 0xFF0000) >> 16); + } + + if (fn[0] && ((sleep_val+fadein_val+fadeout_val)>0)) + { + MSG msg; + static WNDCLASS wc; + wc.lpfnWndProc = WndProc; + wc.hInstance = g_hInstance; + wc.hCursor = LoadCursor(NULL,IDC_ARROW); + wc.lpszClassName = classname; + if (RegisterClass(&wc)) + { + char fn2[MAX_PATH]; + lstrcpy(fn2,fn); + lstrcat(fn,".bmp"); + lstrcat(fn2,".wav"); + g_hbm=LoadImage(NULL,fn,IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION|LR_LOADFROMFILE); + if (g_hbm) + { + HWND myWnd; + UINT timerEvent; + + // Get Bitmap Information + GetObject(g_hbm, sizeof(bm), (LPSTR)&bm); + + myWnd = CreateWindowEx(WS_EX_TOOLWINDOW | ((nt50)?(WS_EX_LAYERED):(0)),classname,classname, + 0,0,0,0,0,(HWND)hwndParent,NULL,g_hInstance,NULL); + + // Set transparency / key color + if (nt50) + { + // Get blending proc address + HANDLE user32 = GetModuleHandle("user32"); + SetLayeredWindowAttributesProc = (_tSetLayeredWindowAttributesProc) GetProcAddress(user32, "SetLayeredWindowAttributes"); + // Use win2k method + SetLayeredWindowAttributesProc(myWnd, keycolor, + (fadein_val > 0)?(0):(255), + alphaparam); + } else + if (keycolor != -1) + { + // transparency mode + SetTransparentRegion(myWnd); + } + + PlaySound(fn2,NULL,SND_ASYNC|SND_FILENAME|SND_NODEFAULT); + + // Start up timer... + state = 0; timeleft = fadein_val; + timerEvent = timeSetEvent(resolution, RESOLUTION/4, TimeProc, (DWORD_PTR)myWnd, TIME_PERIODIC); + + while (IsWindow(myWnd) && GetMessage(&msg,myWnd,0,0)) + { + DispatchMessage(&msg); + } + + // Kill the timer... + timeKillEvent(timerEvent); + + // Stop currently playing wave, we want to exit + PlaySound(0,0,0); + + DeleteObject(g_hbm); + } + + // We should UnRegister class, since Windows NT series never does this by itself + UnregisterClass(wc.lpszClassName, g_hInstance); + } + } + wsprintf(temp,"%d",g_rv); + pushstring(temp); +} + +#ifdef _DEBUG +void main() +{ +} +#endif + int myatoi(char *s) { unsigned int v=0; @@ -120,219 +324,3 @@ int myatoi(char *s) } return (int)v; } - -void CALLBACK TimeProc( - UINT uID, - UINT uMsg, - DWORD dwUser, - DWORD dw1, - DWORD dw2) -{ - switch (state) - { - // FadeIN - case 0: if (timeleft == 0) - { - timeleft = sleep_val; - state++; - if (nt50) call = 255; - } else { call = ((fadein_val-timeleft)*255)/fadein_val; break; } - // Sleep - case 1: if (timeleft == 0) - { - timeleft = fadeout_val; - state++; - } else break; - // FadeOUT - case 2: if (timeleft == 0) - { - PostMessage((HWND)dwUser, WM_TIMER, 0, 0); - return; - } else { call = ((timeleft)*255)/fadeout_val; break; } - } - // Transparency value aquired, and could be set... - if ((call >= 0) && nt50) - SetLayeredWindowAttributesProc((HWND)dwUser, keycolor, - call, - alphaparam); - call = -1; - // Time is running out... - timeleft--; -} - -void __declspec(dllexport) show(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) -{ - char fn[MAX_PATH]; - char temp[64]; - char *sleep=temp; - - EXDLL_INIT(); - - popstring(temp); - sleep_val = myatoi(temp) / RESOLUTION; - popstring(temp); - fadein_val = myatoi(temp) / RESOLUTION; - popstring(temp); - fadeout_val = myatoi(temp) / RESOLUTION; - popstring(temp); - keycolor = myatoi(temp); - popstring(fn); - - // Check for winXP/2k - nt50 = (LOBYTE(LOWORD(GetVersion())) >= 5); - if (!nt50) - { - // Fading is unsupported at old windows versions... - resolution = (sleep_val + fadein_val + fadeout_val) * RESOLUTION; - fadeout_val = fadein_val = 0; - sleep_val = 1; - } else alphaparam = LWA_ALPHA | ((keycolor == -1)?(0):(LWA_COLORKEY)); - - if (fn[0] && sleep_val>0) - { - MSG msg; - char classname[4]="_sp"; - static WNDCLASS wc; - wc.lpfnWndProc = WndProc; - wc.hInstance = g_hInstance; - wc.hCursor = LoadCursor(NULL,IDC_ARROW); - wc.lpszClassName = classname; - if (RegisterClass(&wc)) - { - char fn2[MAX_PATH]; - lstrcpy(fn2,fn); - lstrcat(fn,".bmp"); - lstrcat(fn2,".wav"); - g_hbm=LoadImage(NULL,fn,IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION|LR_LOADFROMFILE); - if (g_hbm) - { - HWND myWnd; - UINT timerEvent; - - // Get Bitmap Information - GetObject(g_hbm, sizeof(bm), (LPSTR)&bm); - - myWnd = CreateWindowEx(WS_EX_TOOLWINDOW | ((nt50)?(WS_EX_LAYERED):(0)),classname,classname, - 0,0,0,0,0,(HWND)hwndParent,NULL,g_hInstance,NULL); - - // Set transparency / key color - if (nt50) - { - // Get blending proc address - HANDLE user32 = GetModuleHandle("user32"); - SetLayeredWindowAttributesProc = (_tSetLayeredWindowAttributesProc) GetProcAddress(user32, "SetLayeredWindowAttributes"); - // Use win2k method - SetLayeredWindowAttributesProc(myWnd, keycolor, - (fadein_val > 0)?(0):(255), - alphaparam); - } else - if (keycolor != -1) - { - // Use simpliest region method - int x, y, wdelta; - HRGN region, cutrgn; - BYTE *bmp = bm.bmBits; - - region = CreateRectRgn(0,0,bm.bmWidth,bm.bmHeight); - //region = CreateRectRgn(0,0,0,0); - - if (bm.bmBitsPixel == 8) - { -#if (KEYCOLOR_8BIT_RGBSUPPORT == 1) - HDC hMemDC; - HBITMAP hOldBitmap; - int rgb[256]; - int nColors; - - // Find out how many colors are in the color table - nColors = 1 << bm.bmBitsPixel; - // Create a memory DC and select the DIBSection into it - hMemDC = CreateCompatibleDC(NULL); - hOldBitmap = SelectObject(hMemDC,g_hbm); - // Get the DIBSection's color table - GetDIBColorTable(hMemDC,0,nColors,(RGBQUAD*)rgb); - - // Find our keycolor at palette - if (keycolor < 0x1000000) - { - for (x = 0; x < nColors; x++) - if (rgb[x] == keycolor) { keycolor = x; break; } - } else keycolor &= 0xff; - - // Free used objects - SelectObject(hMemDC,hOldBitmap); - DeleteDC(hMemDC); -#endif - - // Bitmap is DWORD aligned by width - //wdelta = (( bm.bmWidth + 3 ) & ~3) - bm.bmWidth; - wdelta = ((bm.bmWidth + 3) & 3) ^ 3; - // Search for transparent pixels - for (y = bm.bmHeight-1; y >= 0; y--, bmp += wdelta) - for (x = 0; x < bm.bmWidth; ) - if (*bmp == (BYTE) keycolor) - { - int j = x; - while ((x < bm.bmWidth) && (*bmp == (BYTE) keycolor)) x++, bmp++; - - // Cut transparent pixels from the original region - cutrgn = CreateRectRgn(j, y, x, y+1); - CombineRgn(region, region, cutrgn, RGN_XOR); - DeleteObject(cutrgn); - } else bmp++, x++; - } else if (bm.bmBitsPixel == 24) - { - // Bitmap is DWORD aligned by width - wdelta = ((bm.bmWidth*3 + 3 ) & 3) ^ 3; - // Search for transparent pixels - for (y = bm.bmHeight-1; y >= 0; y--, bmp += wdelta) - for (x = 0; x < bm.bmWidth; ) - if ((*(int*)bmp & 0xFFFFFF) == keycolor) - { - int j = x; - while ((x < bm.bmWidth) && ((*(int*)bmp & 0xFFFFFF) == keycolor)) bmp+=3, x++; - - // Cut transparent pixels from the original region - cutrgn = CreateRectRgn(j, y, x, y+1); - CombineRgn(region, region, cutrgn, RGN_XOR); - DeleteObject(cutrgn); - } else bmp += 3, x++; - } - - // Set resulting region. - SetWindowRgn(myWnd, region, TRUE); - } - - PlaySound(fn2,NULL,SND_ASYNC|SND_FILENAME|SND_NODEFAULT); - - // Start up timer... - state = 0; timeleft = fadein_val; - timerEvent = timeSetEvent(resolution, RESOLUTION/4, TimeProc, (DWORD_PTR)myWnd, TIME_PERIODIC); - - while (IsWindow(myWnd) && GetMessage(&msg,myWnd,0,0)) - { - DispatchMessage(&msg); - } - - // Kill the timer... - timeKillEvent(timerEvent); - - // Stop currently playing wave, we want to exit - PlaySound(0,0,0); - - DeleteObject(g_hbm); - } - - // We should UnRegister class, since Windows NT series never does this by itself - UnregisterClass(wc.lpszClassName, g_hInstance); - } - } - wsprintf(temp,"%d",g_rv); - pushstring(temp); -} - -#ifdef _DEBUG -void main() -{ -} -#endif \ No newline at end of file diff --git a/Contrib/AdvSplash/advsplash.txt b/Contrib/AdvSplash/advsplash.txt index ef0094c3..02da65cb 100644 --- a/Contrib/AdvSplash/advsplash.txt +++ b/Contrib/AdvSplash/advsplash.txt @@ -1,6 +1,6 @@ -AdvSplash.exe - small (6.0k), simple plugin that lets you throw +AdvSplash.exe - small (5.5k), simple plugin that lets you throw up a splash screen in NSIS installers with cool fading effects (win2k/xp) -and transparency (24bit/8bit bitmaps). +and transparency. To use: @@ -31,13 +31,9 @@ Calling format Delay - length to show the screen for (in milliseconds) FadeIn - length to show the fadein scene (in ms) (not included in Delay) FadeOut - length to show the fadeout scene (in ms) (not included in Delay) -KeyColor - color used for transparency. For 24 bit bitmaps could be any RGB - value (for ex. R=255 G=100 B=16 -> KeyColor=0xFF6410), for 8 bit bitmaps - could be either RGB value or index of the color at bitmap palette - (if such RGB color present in your image and you'd like to use palette - index, use (0x1000000+index) as KeyColor [you should calculate - this value by yourself]). Use KeyColor=-1 if there is now transparent - color at your image. +KeyColor - color used for transparency, could be any RGB value + (for ex. R=255 G=100 B=16 -> KeyColor=0xFF6410), + use KeyColor=-1 if there is no transparent color at your image. FileName - splash bitmap filename (without the .bmp). The BMP file used will be this parameter.bmp, and the wave file used (if present) will be this parameter.wav. @@ -45,11 +41,11 @@ FileName - splash bitmap filename (without the .bmp). The BMP file used will be (If you already have an .onInit function, put that in it) Note 1: fadein/fadeout supported only on win2k/winxp systems, all other systems -will show simple splash screen with Delay = Delay + FadeIn + FadeOut. +will show simple splash screen with Delay = Delay + FadeIn + FadeOut. Also, I've +noted my winXP uses no transparent color at 16 bpp, so at bpps lower than 32 +for images with transparent color no fading effect will occur. -Note 2: transparency supported only for 24bit and 8bit bitmaps. - -Note 3: the return value of splash is 1 if the user closed the splash +Note 2: the return value of splash is 1 if the user closed the splash screen early (pop it from the stack) -Justin diff --git a/Plugins/advsplash.dll b/Plugins/advsplash.dll index bdaffb92461e67630ebed9395e9c336fbb3f426f..906d57c6b7c0f2f08d256ac8854c14bb022a938b 100644 GIT binary patch literal 5120 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 KU4q5$E&M-qBGiHa literal 6144 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 EF9e$zSpWb4