diff --git a/widget/src/windows/Makefile.in b/widget/src/windows/Makefile.in index c152bcef7456..c5583cbb4950 100644 --- a/widget/src/windows/Makefile.in +++ b/widget/src/windows/Makefile.in @@ -66,6 +66,7 @@ REQUIRES = xpcom \ layout \ xuldoc \ view \ + imglib2 \ $(NULL) CPPSRCS = \ diff --git a/widget/src/windows/nsWindow.cpp b/widget/src/windows/nsWindow.cpp index eeb589fc7dfe..46e1779d45e1 100644 --- a/widget/src/windows/nsWindow.cpp +++ b/widget/src/windows/nsWindow.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sts=2 sw=2 et cin: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -31,6 +32,7 @@ * Makoto Kato * Masayuki Nakano * Dainis Jonitis + * Christian Biesinger * * 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 @@ -70,6 +72,10 @@ #include "nsColor.h" #include "nsTransform2D.h" #include "nsIEventQueue.h" +#include "imgIContainer.h" +#include "gfxIImageFrame.h" +#include "nsIProperties.h" +#include "nsISupportsPrimitives.h" #include "nsNativeCharsetUtils.h" #include @@ -127,7 +133,7 @@ #include "prprf.h" #include "prmem.h" -static const char *kMozHeapDumpMessageString = "MOZ_HeapDump"; +static const char kMozHeapDumpMessageString[] = "MOZ_HeapDump"; #define kWindowPositionSlop 20 @@ -171,6 +177,24 @@ static inline PRBool IsAlphaTranslucencySupported() { return pUpdateLayeredWindo #endif +static PRBool IsCursorTranslucencySupported() { + static didCheck = PR_FALSE; + static isSupported = PR_FALSE; + if (!didCheck) { + didCheck = PR_TRUE; + // Cursor translucency is supported on Windows XP and newer + OSVERSIONINFO osversion; + memset(&osversion, 0, sizeof(OSVERSIONINFO)); + osversion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if (GetVersionEx(&osversion)) + isSupported = osversion.dwMajorVersion > 5 || // Newer Windows versions + osversion.dwMajorVersion == 5 && + osversion.dwMinorVersion >= 1; // WinXP, Server 2003 + } + + return isSupported; +} + // Pick some random timer ID. Is there a better way? #define NS_FLASH_TIMER_ID 0x011231984 @@ -582,7 +606,7 @@ static PRBool LangIDToCP(WORD aLangID, UINT& oCP) oCP = atoi(cp_name); if (cp_name != cp_on_stack) delete [] cp_name; - return PR_TRUE; + return PR_TRUE; } else { oCP = CP_ACP; return PR_FALSE; @@ -2588,7 +2612,7 @@ NS_METHOD nsWindow::SetCursor(nsCursor aCursor) break; default: - NS_ASSERTION(0, "Invalid cursor type"); + NS_ERROR("Invalid cursor type"); break; } @@ -2600,6 +2624,256 @@ NS_METHOD nsWindow::SetCursor(nsCursor aCursor) return NS_OK; } +// static +PRUint8* nsWindow::DataToAData(PRUint8* aImageData, PRUint32 aImageBytesPerRow, + PRUint8* aAlphaData, PRUint32 aAlphaBytesPerRow, + PRUint32 aWidth, PRUint32 aHeight) +{ + // We will have 32 bpp, so bytes per row will be 4 * w + PRUint32 outBpr = aWidth * 4; + + // Avoid overflows + if (aWidth > 0xfff || aHeight > 0xfff) + return NULL; + + PRUint8* outData = new PRUint8[outBpr * aHeight]; + if (!outData) + return NULL; + + PRUint8 *outRow = outData, + *imageRow = aImageData, + *alphaRow = aAlphaData; + for (PRUint32 curRow = 0; curRow < aHeight; curRow++) { + PRUint8 *irow = imageRow, *arow = alphaRow; + for (PRUint32 curCol = 0; curCol < aWidth; curCol++) { + *outRow++ = *imageRow++; // B + *outRow++ = *imageRow++; // G + *outRow++ = *imageRow++; // R + *outRow++ = *alphaRow++; // A + } + imageRow = irow + aImageBytesPerRow; + alphaRow = arow + aAlphaBytesPerRow; + } + return outData; +} + +// static +HBITMAP nsWindow::DataToBitmap(PRUint8* aImageData, + PRUint32 aWidth, + PRUint32 aHeight, + PRUint32 aDepth) +{ + HDC dc = ::GetDC(NULL); + if (aDepth == 32 && IsCursorTranslucencySupported()) { + // Alpha channel. We need the new header. + BITMAPV4HEADER head = { 0 }; + head.bV4Size = sizeof(head); + head.bV4Width = aWidth; + head.bV4Height = aHeight; + head.bV4Planes = 1; + head.bV4BitCount = aDepth; + head.bV4V4Compression = BI_BITFIELDS; + head.bV4SizeImage = 0; // Uncompressed + head.bV4XPelsPerMeter = 0; + head.bV4YPelsPerMeter = 0; + head.bV4ClrUsed = 0; + head.bV4ClrImportant = 0; + + head.bV4RedMask = 0x00FF0000; + head.bV4GreenMask = 0x0000FF00; + head.bV4BlueMask = 0x000000FF; + head.bV4AlphaMask = 0xFF000000; + + HBITMAP bmp = ::CreateDIBitmap(dc, + NS_REINTERPRET_CAST(CONST BITMAPINFOHEADER*, &head), + CBM_INIT, + aImageData, + NS_REINTERPRET_CAST(CONST BITMAPINFO*, &head), + DIB_RGB_COLORS); + ::ReleaseDC(NULL, dc); + return bmp; + } + + + BITMAPINFOHEADER head = { 0 }; + + head.biSize = sizeof(head); + head.biWidth = aWidth; + head.biHeight = aHeight; + head.biPlanes = 1; + head.biBitCount = aDepth; + head.biCompression = BI_RGB; + head.biSizeImage = 0; // Uncompressed + head.biXPelsPerMeter = 0; + head.biYPelsPerMeter = 0; + head.biClrUsed = (aDepth == 1) ? 2 : 0; + head.biClrImportant = 0; + + char reserved_space[sizeof(BITMAPINFO) + sizeof(RGBQUAD) * 256]; + BITMAPINFO& bi = *(BITMAPINFO*)reserved_space; + + bi.bmiHeader = head; + + if (aDepth == 1) { + RGBQUAD black = { 0, 0, 0, 0 }; + RGBQUAD white = { 255, 255, 255, 0 }; + + bi.bmiColors[0] = white; + bi.bmiColors[1] = black; + } else { + for (int i = 0; i < 256; i++) { + RGBQUAD cur = { i, i, i, 0 }; + bi.bmiColors[255 - i] = cur; + } + } + + HBITMAP bmp = ::CreateDIBitmap(dc, &head, CBM_INIT, aImageData, &bi, DIB_RGB_COLORS); + ::ReleaseDC(NULL, dc); + return bmp; +} + +// static +HBITMAP nsWindow::CreateOpaqueAlphaChannel(PRUint32 aWidth, PRUint32 aHeight) +{ + // Make up an opaque alpha channel + PRUint32 abpr = ((aWidth / 8) + 3) & ~3; + PRUint8* opaque = (PRUint8*)malloc(abpr * aHeight); + if (!opaque) + return NULL; + + memset(opaque, 0xff, abpr * aHeight); + + HBITMAP hAlpha = DataToBitmap(opaque, aWidth, aHeight, 1); + free(opaque); + return hAlpha; +} + +NS_IMETHODIMP nsWindow::SetCursor(imgIContainer* aCursor) +{ + // Get the image data + nsCOMPtr frame; + aCursor->GetFrameAt(0, getter_AddRefs(frame)); + if (!frame) + return NS_ERROR_NOT_AVAILABLE; + + PRInt32 width, height; + frame->GetWidth(&width); + frame->GetHeight(&height); + + PRUint32 hotspotX = 0, hotspotY = 0; + + nsCOMPtr props(do_QueryInterface(aCursor)); + if (props) { + nsCOMPtr hotspotXWrap, hotspotYWrap; + + props->Get("hotspotX", NS_GET_IID(nsISupportsPRUint32), getter_AddRefs(hotspotXWrap)); + props->Get("hotspotY", NS_GET_IID(nsISupportsPRUint32), getter_AddRefs(hotspotYWrap)); + + if (hotspotXWrap) + hotspotXWrap->GetData(&hotspotX); + if (hotspotYWrap) + hotspotYWrap->GetData(&hotspotY); + } + + gfx_format format; + nsresult rv = frame->GetFormat(&format); + if (NS_FAILED(rv)) + return rv; + + if (format != gfxIFormats::BGR_A1 && format != gfxIFormats::BGR_A8 && + format != gfxIFormats::BGR) + return NS_ERROR_UNEXPECTED; + + PRUint32 bpr; + rv = frame->GetImageBytesPerRow(&bpr); + if (NS_FAILED(rv)) + return rv; + + frame->LockImageData(); + PRUint32 dataLen; + PRUint8* data; + rv = frame->GetImageData(&data, &dataLen); + if (NS_FAILED(rv)) { + frame->UnlockImageData(); + return rv; + } + + HBITMAP hBMP = NULL; + if (format != gfxIFormats::BGR_A8) { + hBMP = DataToBitmap(data, width, height, 24); + if (hBMP == NULL) { + frame->UnlockImageData(); + return NS_ERROR_FAILURE; + } + } + + HBITMAP hAlpha = NULL; + if (format == gfxIFormats::BGR) { + hAlpha = CreateOpaqueAlphaChannel(width, height); + } else { + PRUint32 abpr; + rv = frame->GetAlphaBytesPerRow(&abpr); + if (NS_FAILED(rv)) { + frame->UnlockImageData(); + if (hBMP != NULL) + ::DeleteObject(hBMP); + return rv; + } + + PRUint8* adata; + frame->LockAlphaData(); + rv = frame->GetAlphaData(&adata, &dataLen); + if (NS_FAILED(rv)) { + if (hBMP != NULL) + ::DeleteObject(hBMP); + frame->UnlockImageData(); + frame->UnlockAlphaData(); + return rv; + } + + if (format == gfxIFormats::BGR_A8) { + PRUint8* bgra8data = DataToAData(data, bpr, adata, abpr, width, height); + if (bgra8data) { + hBMP = DataToBitmap(bgra8data, width, height, 32); + if (hBMP != NULL) { + hAlpha = CreateOpaqueAlphaChannel(width, height); + } + } + } else { + hAlpha = DataToBitmap(adata, width, height, 1); + } + + frame->UnlockAlphaData(); + } + frame->UnlockImageData(); + if (hBMP == NULL) { + return NS_ERROR_FAILURE; + } + if (hAlpha == NULL) { + ::DeleteObject(hBMP); + return NS_ERROR_FAILURE; + } + + ICONINFO info = {0}; + info.fIcon = FALSE; + info.xHotspot = hotspotX; + info.yHotspot = hotspotY; + info.hbmMask = hAlpha; + info.hbmColor = hBMP; + + HCURSOR cursor = ::CreateIconIndirect(&info); + ::DeleteObject(hBMP); + ::DeleteObject(hAlpha); + if (cursor == NULL) { + return NS_ERROR_FAILURE; + } + + mCursor = nsCursor(-1); + ::SetCursor(cursor); + ::DestroyIcon(cursor); + return NS_OK; +} + NS_IMETHODIMP nsWindow::HideWindowChrome(PRBool aShouldHide) { HWND hwnd = GetTopLevelHWND(mWnd); diff --git a/widget/src/windows/nsWindow.h b/widget/src/windows/nsWindow.h index bad7fcae5eb5..caeb79d8c1f5 100644 --- a/widget/src/windows/nsWindow.h +++ b/widget/src/windows/nsWindow.h @@ -65,6 +65,8 @@ class nsIRollupListener; class nsIMenuBar; class nsIFile; +class imgIContainer; + #ifdef ACCESSIBILITY #include "OLEACC.H" #include "nsIAccessible.h" @@ -342,6 +344,7 @@ public: virtual nsIFontMetrics* GetFont(void); NS_IMETHOD SetFont(const nsFont &aFont); NS_IMETHOD SetCursor(nsCursor aCursor); + NS_IMETHOD SetCursor(imgIContainer* aCursor); NS_IMETHOD HideWindowChrome(PRBool aShouldHide); NS_IMETHOD Validate(); NS_IMETHOD Invalidate(PRBool aIsSynchronous); @@ -642,6 +645,45 @@ protected: // Heap dump static UINT uWM_HEAP_DUMP; // Dump heap to a file + /** + * Combine the given image data with a separate alpha channel to image data + * with the alpha channel interleaved with the image data (BGRA). + * + * @return BGRA data. Must be delete[]d. On failure, NULL will be returned. + */ + static PRUint8* DataToAData(PRUint8* aImageData, PRUint32 aImageBytesPerRow, + PRUint8* aAlphaData, PRUint32 aAlphaBytesPerRow, + PRUint32 aWidth, PRUint32 aHeight); + /** + * Convert the given image data to a HBITMAP. If the requested depth is + * 32 bit and the OS supports translucency, a bitmap with an alpha channel + * will be returned. + * + * @param aImageData The image data to convert. Must use the format accepted + * by CreateDIBitmap. + * @param aWidth With of the bitmap, in pixels. + * @param aHeight Height of the image, in pixels. + * @param aDepth Image depth, in bits. Should be one of 1, 24 and 32. + * + * @return The HBITMAP representing the image. Caller should call + * DeleteObject when done with the bitmap. + * On failure, NULL will be returned. + */ + static HBITMAP DataToBitmap(PRUint8* aImageData, + PRUint32 aWidth, + PRUint32 aHeight, + PRUint32 aDepth); + + /** + * Create a bitmap representing an opaque alpha channel (filled with 0xff). + * @param aWidth Desired with of the bitmap + * @param aHeight Desired height of the bitmap + * @return The bitmap. Caller should call DeleteObject when done with + * the bitmap. On failure, NULL will be returned. + */ + static HBITMAP CreateOpaqueAlphaChannel(PRUint32 aWidth, PRUint32 aHeight); + + #ifdef ACCESSIBILITY nsIAccessible* mRootAccessible; static BOOL gIsAccessibilityOn;