diff --git a/gfx/src/nsBlender.cpp b/gfx/src/nsBlender.cpp index c49afc7d5f8..49539b9b30d 100644 --- a/gfx/src/nsBlender.cpp +++ b/gfx/src/nsBlender.cpp @@ -387,6 +387,8 @@ NS_IMETHODIMP nsBlender::GetAlphas(const nsRect& aRect, nsIDrawingSurface* aBlac } else { result = NS_ERROR_FAILURE; } + } else { + result = NS_ERROR_FAILURE; } whiteSurface->Unlock(); diff --git a/widget/src/windows/nsWindow.cpp b/widget/src/windows/nsWindow.cpp index 1dc9bab374d..d2dad18b3ff 100644 --- a/widget/src/windows/nsWindow.cpp +++ b/widget/src/windows/nsWindow.cpp @@ -30,6 +30,7 @@ * Roy Yokoyama * Makoto Kato * Masayuki Nakano + * Dainis Jonitis * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -66,6 +67,7 @@ #include "nsIDeviceContext.h" #include "nsIScreenManager.h" #include "nsRect.h" +#include "nsColor.h" #include "nsTransform2D.h" #include "nsIEventQueue.h" #include @@ -132,6 +134,19 @@ static const char *kMozHeapDumpMessageString = "MOZ_HeapDump"; #define SPI_GETWHEELSCROLLLINES 104 #endif +#ifndef AC_SRC_ALPHA +#define AC_SRC_ALPHA 0x01 +#endif + +#ifndef WS_EX_LAYERED +#define WS_EX_LAYERED 0x00080000 +#endif + +#ifndef ULW_ALPHA +#define ULW_ALPHA 0x00000002 +#endif + + // Pick some random timer ID. Is there a better way? #define NS_FLASH_TIMER_ID 0x011231984 @@ -544,6 +559,20 @@ static PRBool LangIDToCP(WORD aLangID, UINT& oCP) } } +static HWND GetTopLevelHWND(HWND aWnd) +{ + HWND curWnd = aWnd; + HWND topWnd = NULL; + + while (curWnd) + { + topWnd = curWnd; + curWnd = ::GetParent(curWnd); + } + + return topWnd; +} + /* This object maintains a correlation between attention timers and the windows to which they belong. It's lighter than a hashtable (expected usage is really just one at a time) and allows nsWindow::GetNSWindowPtr @@ -760,6 +789,12 @@ nsWindow::nsWindow() : nsBaseWidget() mFont = nsnull; mIsVisible = PR_FALSE; mHas3DBorder = PR_FALSE; +#ifdef MOZ_XUL + mIsTranslucent = PR_FALSE; + mMemoryBitmap = NULL; + mMemoryDC = NULL; + mAlphaMask = nsnull; +#endif mWindowType = eWindowType_child; mBorderStyle = eBorderStyle_default; mBorderlessParent = 0; @@ -1396,6 +1431,8 @@ nsresult nsWindow::StandardWindowCreate(nsIWidget *aParent, nsnull : aParent; mIsTopWidgetWindow = (nsnull == baseParent); + mBounds.width = aRect.width; + mBounds.height = aRect.height; BaseCreate(baseParent, aRect, aHandleEventFunction, aContext, aAppShell, aToolkit, aInitData); @@ -1639,6 +1676,19 @@ NS_METHOD nsWindow::Destroy() if (icon) ::DestroyIcon(icon); +#ifdef MOZ_XUL + if (mIsTranslucent) + { + ::DeleteDC(mMemoryDC); + ::DeleteObject(mMemoryBitmap); + delete [] mAlphaMask; + + mMemoryDC = NULL; + mMemoryBitmap = NULL; + mAlphaMask = nsnull; + } +#endif + VERIFY(::DestroyWindow(mWnd)); mWnd = NULL; @@ -2053,6 +2103,12 @@ NS_METHOD nsWindow::Resize(PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint) { NS_ASSERTION((aWidth >=0 ) , "Negative width passed to nsWindow::Resize"); NS_ASSERTION((aHeight >=0 ), "Negative height passed to nsWindow::Resize"); + +#ifdef MOZ_XUL + if (mIsTranslucent) + ResizeTranslucentWindow(aWidth, aHeight); +#endif + // Set cached value for lightweight and printing mBounds.width = aWidth; mBounds.height = aHeight; @@ -2103,6 +2159,11 @@ NS_METHOD nsWindow::Resize(PRInt32 aX, NS_ASSERTION((aWidth >=0 ), "Negative width passed to nsWindow::Resize"); NS_ASSERTION((aHeight >=0 ), "Negative height passed to nsWindow::Resize"); +#ifdef MOZ_XUL + if (mIsTranslucent) + ResizeTranslucentWindow(aWidth, aHeight); +#endif + // Set cached value for lightweight and printing mBounds.x = aX; mBounds.y = aY; @@ -2528,7 +2589,7 @@ NS_IMETHODIMP nsWindow::HideWindowChrome(PRBool aShouldHide) DWORD tempExStyle = nsToolkit::mGetWindowLong(hwnd, GWL_EXSTYLE); style = WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX; - exStyle = 0; + exStyle = tempExStyle & WS_EX_LAYERED; mOldStyle = tempStyle; mOldExStyle = tempExStyle; @@ -2580,12 +2641,18 @@ NS_METHOD nsWindow::Invalidate(PRBool aIsSynchronous) (PRInt32) mWnd); #endif // NS_DEBUG +#ifdef MOZ_XUL + if (mIsTranslucent) + OnPaint(mMemoryDC); + else +#endif + { VERIFY(::InvalidateRect(mWnd, NULL, TRUE)); if (aIsSynchronous) { VERIFY(::UpdateWindow(mWnd)); } } - + } return NS_OK; } @@ -2596,15 +2663,8 @@ NS_METHOD nsWindow::Invalidate(PRBool aIsSynchronous) //------------------------------------------------------------------------- NS_METHOD nsWindow::Invalidate(const nsRect & aRect, PRBool aIsSynchronous) { - RECT rect; - if (mWnd) { - rect.left = aRect.x; - rect.top = aRect.y; - rect.right = aRect.x + aRect.width; - rect.bottom = aRect.y + aRect.height; - #ifdef NS_DEBUG debug_DumpInvalidate(stdout, this, @@ -2614,11 +2674,25 @@ NS_METHOD nsWindow::Invalidate(const nsRect & aRect, PRBool aIsSynchronous) (PRInt32) mWnd); #endif // NS_DEBUG +#ifdef MOZ_XUL + if (mIsTranslucent) + OnPaint(mMemoryDC); + else +#endif + { + RECT rect; + + rect.left = aRect.x; + rect.top = aRect.y; + rect.right = aRect.x + aRect.width; + rect.bottom = aRect.y + aRect.height; + VERIFY(::InvalidateRect(mWnd, &rect, TRUE)); if (aIsSynchronous) { VERIFY(::UpdateWindow(mWnd)); } } + } return NS_OK; } @@ -2628,6 +2702,12 @@ nsWindow::InvalidateRegion(const nsIRegion *aRegion, PRBool aIsSynchronous) { nsresult rv = NS_OK; if (mWnd) { +#ifdef MOZ_XUL + if (mIsTranslucent) + OnPaint(mMemoryDC); + else +#endif + { HRGN nativeRegion; rv = aRegion->GetNativeRegion((void *&)nativeRegion); if (nativeRegion) { @@ -2642,6 +2722,7 @@ nsWindow::InvalidateRegion(const nsIRegion *aRegion, PRBool aIsSynchronous) rv = NS_ERROR_FAILURE; } } + } return rv; } @@ -2652,11 +2733,23 @@ nsWindow::InvalidateRegion(const nsIRegion *aRegion, PRBool aIsSynchronous) //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::Update() { + nsresult rv = NS_OK; + // updates can come through for windows no longer holding an mWnd during // deletes triggered by JavaScript in buttons with mouse feedback if (mWnd) + { +#ifdef MOZ_XUL + if (mIsTranslucent) + { +// rv = UpdateTranslucentWindow(); + } else +#endif + { VERIFY(::UpdateWindow(mWnd)); - return NS_OK; + } + } + return rv; } //------------------------------------------------------------------------- @@ -2673,7 +2766,11 @@ void* nsWindow::GetNativeData(PRUint32 aDataType) return (void*)mWnd; case NS_NATIVE_GRAPHIC: // XXX: This is sleezy!! Remember to Release the DC after using it! +#ifdef MOZ_XUL + return (void*)(!mIsTranslucent) ? ::GetDC(mWnd) : mMemoryDC; +#else return (void*)::GetDC(mWnd); +#endif case NS_NATIVE_COLORMAP: default: break; @@ -2688,7 +2785,13 @@ void nsWindow::FreeNativeData(void * data, PRUint32 aDataType) switch(aDataType) { case NS_NATIVE_GRAPHIC: +#ifdef MOZ_XUL + if (!mIsTranslucent) ::ReleaseDC(mWnd, (HDC)data); +#else + ::ReleaseDC(mWnd, (HDC)data); +#endif + break; case NS_NATIVE_WIDGET: case NS_NATIVE_WINDOW: case NS_NATIVE_PLUGIN_PORT: @@ -4221,6 +4324,11 @@ PRBool nsWindow::ProcessMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT ::RedrawWindow(mWnd, &drect, NULL, RDW_INVALIDATE | RDW_NOERASE | RDW_NOINTERNALPAINT | RDW_ERASENOW | RDW_ALLCHILDREN); } + +#ifdef MOZ_XUL + if (mIsTranslucent) + ResizeTranslucentWindow(newWidth, newHeight); +#endif mBounds.width = newWidth; mBounds.height = newHeight; mLastSize.width = newWidth; @@ -5045,6 +5153,16 @@ PRBool nsWindow::OnPaint(HDC aDC) event.renderingContext->Init(mContext, surf); result = DispatchWindowEvent(&event, eventStatus); event.renderingContext->DestroyDrawingSurface(surf); + +#ifdef MOZ_XUL + if (mIsTranslucent) + { + // Data from offscreen drawing surface was copied to memory bitmap of transparent + // bitmap. Now it can be read from memory bitmap to apply alpha channel and after + // that displayed on the screen. + UpdateTranslucentWindow(); + } +#endif } NS_RELEASE(winrc); @@ -7146,3 +7264,340 @@ STDMETHODIMP_(LRESULT) nsWindow::LresultFromObject(REFIID riid, return 0; } #endif + +#ifdef MOZ_XUL + +typedef WINUSERAPI BOOL WINAPI UpdateLayeredWindowProc (HWND hWnd, HDC hdcDst, POINT *pptDst, + SIZE *psize, HDC hdcSrc, POINT *pptSrc, + COLORREF crKey, BLENDFUNCTION *pblend, + DWORD dwFlags); + + +static UpdateLayeredWindowProc* pUpdateLayeredWindow = NULL; + + +static PRBool IsTranslucencySupported() +{ + static PRBool firstTime = PR_TRUE; + + if (firstTime) + { + firstTime = PR_FALSE; + + HMODULE user32 = ::GetModuleHandle("user32.dll"); + + if (user32) + pUpdateLayeredWindow = (UpdateLayeredWindowProc*)::GetProcAddress(user32, "UpdateLayeredWindow"); + } + + return pUpdateLayeredWindow != NULL; +} + +nsIWidget* nsWindow::GetTopLevelWidget() +{ + nsIWidget* curWidget = this; + NS_ADDREF(curWidget); + + while (PR_TRUE) + { + if (mIsTopWidgetWindow) + return curWidget; + + nsIWidget* parentWidget = curWidget->GetParent(); + + if (parentWidget) + { + NS_RELEASE(curWidget); + curWidget = parentWidget; + } else + return curWidget; + } +} + +void nsWindow::ResizeTranslucentWindow(PRInt32 aNewWidth, PRInt32 aNewHeight) +{ + if (aNewWidth == mBounds.width && aNewHeight == mBounds.height) + return; + + // resize the alpha mask + PRUint8* pBits; + + if (aNewWidth > 0 && aNewHeight > 0) + { + pBits = new PRUint8 [aNewWidth * aNewHeight]; + + if (pBits) + { + PRInt32 copyWidth, copyHeight; + PRInt32 growWidth, growHeight; + + if (aNewWidth > mBounds.width) + { + copyWidth = mBounds.width; + growWidth = aNewWidth - mBounds.width; + } else + { + copyWidth = aNewWidth; + growWidth = 0; + } + + if (aNewHeight > mBounds.height) + { + copyHeight = mBounds.height; + growHeight = aNewHeight - mBounds.height; + } else + { + copyHeight = aNewHeight; + growHeight = 0; + } + + PRUint8* pSrc = mAlphaMask; + PRUint8* pDest = pBits; + + for (PRInt32 cy = 0 ; cy < copyHeight ; cy++) + { + memcpy (pDest, pSrc, copyWidth); + memset (pDest + copyWidth, 255, growWidth); + pSrc += mBounds.width; + pDest += aNewWidth; + } + + for (PRInt32 gy = 0 ; gy < growHeight ; gy++) + { + memset (pDest, 255, aNewWidth); + pDest += aNewWidth; + } + } + } else + pBits = nsnull; + + delete [] mAlphaMask; + mAlphaMask = pBits; + + + // resize the memory bitmap + HDC hScreenDC = ::GetDC(NULL); + mMemoryBitmap = ::CreateCompatibleBitmap(hScreenDC, aNewWidth, aNewHeight); + + if (mMemoryBitmap) + { + HGDIOBJ oldBitmap = ::SelectObject(mMemoryDC, mMemoryBitmap); + ::DeleteObject(oldBitmap); + } + + ::ReleaseDC(NULL, hScreenDC); +} + +NS_IMETHODIMP nsWindow::GetWindowTranslucency(PRBool& aTranslucent) +{ + if (IsTranslucencySupported()) + { + nsWindow* topWindow = (nsWindow*)GetTopLevelWidget(); + aTranslucent = topWindow->GetWindowTranslucencyInner(); + NS_RELEASE(topWindow); + } else + aTranslucent = PR_FALSE; + + return NS_OK; +} + +NS_IMETHODIMP nsWindow::SetWindowTranslucency(PRBool aTranslucent) +{ + if (!IsTranslucencySupported()) + return NS_ERROR_NOT_IMPLEMENTED; + + nsWindow* topWindow = (nsWindow*)GetTopLevelWidget(); + nsresult rv = topWindow->SetWindowTranslucencyInner(aTranslucent); + NS_RELEASE(topWindow); + + return rv; +} + +NS_IMETHODIMP nsWindow::UpdateTranslucentWindowAlpha(const nsRect& aRect, PRUint8* aAlphas) +{ + if (!IsTranslucencySupported()) + return NS_ERROR_NOT_IMPLEMENTED; + + nsWindow* topWindow = (nsWindow*)GetTopLevelWidget(); + topWindow->UpdateTranslucentWindowAlphaInner(aRect, aAlphas); + NS_RELEASE(topWindow); + + return NS_OK; +} + +nsresult nsWindow::SetWindowTranslucencyInner(PRBool aTranslucent) +{ + if (aTranslucent == mIsTranslucent) + return NS_OK; + + HWND hWnd = GetTopLevelHWND(mWnd); + LONG style; + LONG exStyle = nsToolkit::mGetWindowLong(hWnd, GWL_EXSTYLE); + if (aTranslucent) + { + style = nsToolkit::mGetWindowLong(hWnd, GWL_STYLE) & ~WS_CAPTION; + exStyle |= WS_EX_LAYERED; + } else + { + style = WindowStyle(); + exStyle &= ~WS_EX_LAYERED; + } + nsToolkit::mSetWindowLong(hWnd, GWL_STYLE, style); + nsToolkit::mSetWindowLong(hWnd, GWL_EXSTYLE, exStyle); + + nsresult rv = NS_ERROR_FAILURE; + mIsTranslucent = aTranslucent; + + if (aTranslucent) + { + HDC hScreenDC = ::GetDC(NULL); + mMemoryDC = ::CreateCompatibleDC(hScreenDC); + + if (mMemoryDC) + { + mMemoryBitmap = ::CreateCompatibleBitmap(hScreenDC, mBounds.width, mBounds.height); + + if (mMemoryBitmap) + { + ::SelectObject(mMemoryDC, mMemoryBitmap); + + rv = NS_OK; + + if (!mBounds.IsEmpty()) + { + PRInt32 alphaBytes = mBounds.width * mBounds.height; + mAlphaMask = new PRUint8 [alphaBytes]; + + if (mAlphaMask) + memset (mAlphaMask, 255, alphaBytes); + else + rv = NS_ERROR_OUT_OF_MEMORY; + } else + mAlphaMask = nsnull; + } + } + + ::ReleaseDC(NULL, hScreenDC); + } else + { + ::DeleteDC(mMemoryDC); + ::DeleteObject(mMemoryBitmap); + delete [] mAlphaMask; + + mMemoryDC = NULL; + mMemoryBitmap = NULL; + mAlphaMask = nsnull; + + rv = NS_OK; + } + + return rv; +} + +void nsWindow::UpdateTranslucentWindowAlphaInner(const nsRect& aRect, PRUint8* aAlphas) +{ + NS_ASSERTION(mIsTranslucent, "Window is not transparent"); + NS_ASSERTION(aRect.x >= 0 && aRect.y >= 0 && + aRect.XMost() <= mBounds.width && aRect.YMost() <= mBounds.height, + "Rect is out of window bounds"); + + if (!aRect.IsEmpty()) + { + PRUint8* pSrc = aAlphas; + PRUint8* pDest = mAlphaMask + aRect.y * mBounds.width + aRect.x; + + for (PRInt32 y = 0 ; y < aRect.height ; y++) + { + memcpy (pDest, pSrc, aRect.width); + + pSrc += aRect.width; + pDest += mBounds.width; + } + } + + // The real screen update is performed in OnPaint() handler only after rendered + // bits from offscreen drawing surface are copied back to memory bitmap. +} + +nsresult nsWindow::UpdateTranslucentWindow() +{ + if (mBounds.IsEmpty()) + return NS_OK; + + nsresult rv = NS_ERROR_FAILURE; + + // Memory bitmap with alpha channel + HDC hMemoryDC = ::CreateCompatibleDC(NULL); + + if (hMemoryDC) + { + HBITMAP hAlphaBitmap = ::CreateBitmap(mBounds.width, mBounds.height, 1, 32, NULL); + + if (hAlphaBitmap) + { + ::SelectObject(hMemoryDC, hAlphaBitmap); + + BITMAPINFO bi = { 0 }; + bi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); + bi.bmiHeader.biWidth = mBounds.width; + bi.bmiHeader.biHeight = -mBounds.height; + bi.bmiHeader.biPlanes = 1; + bi.bmiHeader.biBitCount = 32; + bi.bmiHeader.biCompression = BI_RGB; + + PRUint8* pBits = new PRUint8 [4 * mBounds.width * mBounds.height]; + + if (pBits) + { + int lines = ::GetDIBits(mMemoryDC, mMemoryBitmap, 0, mBounds.height, pBits, &bi, DIB_RGB_COLORS); + + if (lines == mBounds.height) + { + PRUint8* pPixel = pBits; + PRUint8* pAlpha = mAlphaMask; + + for (PRInt32 cnt = 0 ; cnt < mBounds.width * mBounds.height ; cnt++) + { + // Each of the RGB components should be premultiplied with alpha and divided by 255 + FAST_DIVIDE_BY_255(pPixel [0], *pAlpha * pPixel [0]); + FAST_DIVIDE_BY_255(pPixel [1], *pAlpha * pPixel [1]); + FAST_DIVIDE_BY_255(pPixel [2], *pAlpha * pPixel [2]); + pPixel [3] = *pAlpha; + + pPixel +=4; + pAlpha++; + } + + lines = ::SetDIBits (hMemoryDC, hAlphaBitmap, 0, mBounds.height, pBits, &bi, DIB_RGB_COLORS); + } + + delete [] pBits; + + if (lines == mBounds.height) + { + BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; + SIZE winSize = { mBounds.width, mBounds.height }; + POINT srcPos = { 0, 0 }; + HDC hScreenDC = ::GetDC(NULL); + HWND hWnd = GetTopLevelHWND(mWnd); + RECT winRect; + ::GetWindowRect(hWnd, &winRect); + + // perform the alpha blend + if (pUpdateLayeredWindow(hWnd, hScreenDC, (POINT*)&winRect, &winSize, hMemoryDC, &srcPos, 0, &bf, ULW_ALPHA)) + rv = NS_OK; + + ::ReleaseDC(NULL, hScreenDC); + } + } else + rv = NS_ERROR_OUT_OF_MEMORY; + + ::DeleteObject(hAlphaBitmap); + } + ::DeleteDC(hMemoryDC); + } + + return rv; +} + +#endif diff --git a/widget/src/windows/nsWindow.h b/widget/src/windows/nsWindow.h index 6d971c4c2c8..c5835827eb7 100644 --- a/widget/src/windows/nsWindow.h +++ b/widget/src/windows/nsWindow.h @@ -23,6 +23,7 @@ * Robert O'Callahan * Dean Tessman * Makoto Kato + * Dainis Jonitis * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -391,6 +392,20 @@ public: NS_IMETHOD GetAttention(PRInt32 aCycleCount); NS_IMETHOD GetLastInputEventTime(PRUint32& aTime); +#ifdef MOZ_XUL + NS_IMETHOD SetWindowTranslucency(PRBool aTransparent); + NS_IMETHOD GetWindowTranslucency(PRBool& aTransparent); + NS_IMETHOD UpdateTranslucentWindowAlpha(const nsRect& aRect, PRUint8* aAlphas); +private: + nsresult SetWindowTranslucencyInner(PRBool aTransparent); + PRBool GetWindowTranslucencyInner() { return mIsTranslucent; } + void UpdateTranslucentWindowAlphaInner(const nsRect& aRect, PRUint8* aAlphas); + nsIWidget* GetTopLevelWidget(); + void ResizeTranslucentWindow(PRInt32 aNewWidth, PRInt32 aNewHeight); + nsresult UpdateTranslucentWindow(); +public: +#endif + // nsIKBStateControl interface NS_IMETHOD ResetInputState(); @@ -400,8 +415,6 @@ public: PRBool HandleMouseActionOfIME(PRInt32 aAction, POINT* ptPos); void GetCompositionWindowPos(HIMC hIMC, PRUint32 aEventType, COMPOSITIONFORM *cpForm); - HWND mBorderlessParent; - // nsSwitchToUIThread interface virtual BOOL CallMethod(MethodInfo *info); @@ -522,12 +535,19 @@ protected: static nsWindow* gCurrentWindow; nsPoint mLastPoint; HWND mWnd; + HWND mBorderlessParent; #if 0 HPALETTE mPalette; #endif WNDPROC mPrevWndProc; HBRUSH mBrush; +#ifdef MOZ_XUL + HBITMAP mMemoryBitmap; + HDC mMemoryDC; + PRUint8* mAlphaMask; + PRPackedBool mIsTranslucent; +#endif PRPackedBool mIsTopWidgetWindow; PRPackedBool mHas3DBorder; PRPackedBool mIsShiftDown;