michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * nsWindowGfx - Painting and aceleration. michael@0: */ michael@0: michael@0: // XXX Future: this should really be a stand alone class stored as michael@0: // a member of nsWindow with getters and setters for things like render michael@0: // mode and methods for handling paint. michael@0: michael@0: /************************************************************** michael@0: ************************************************************** michael@0: ** michael@0: ** BLOCK: Includes michael@0: ** michael@0: ** Include headers. michael@0: ** michael@0: ************************************************************** michael@0: **************************************************************/ michael@0: michael@0: #include "mozilla/plugins/PluginInstanceParent.h" michael@0: using mozilla::plugins::PluginInstanceParent; michael@0: michael@0: #include "nsWindowGfx.h" michael@0: #include michael@0: #include "gfxImageSurface.h" michael@0: #include "gfxUtils.h" michael@0: #include "gfxWindowsSurface.h" michael@0: #include "gfxWindowsPlatform.h" michael@0: #include "mozilla/gfx/2D.h" michael@0: #include "mozilla/gfx/DataSurfaceHelpers.h" michael@0: #include "mozilla/gfx/Tools.h" michael@0: #include "mozilla/RefPtr.h" michael@0: #include "nsGfxCIID.h" michael@0: #include "gfxContext.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "prmem.h" michael@0: #include "WinUtils.h" michael@0: #include "nsIWidgetListener.h" michael@0: #include "mozilla/unused.h" michael@0: michael@0: #ifdef MOZ_ENABLE_D3D9_LAYER michael@0: #include "LayerManagerD3D9.h" michael@0: #endif michael@0: #ifdef MOZ_ENABLE_D3D10_LAYER michael@0: #include "LayerManagerD3D10.h" michael@0: #endif michael@0: #include "mozilla/layers/CompositorParent.h" michael@0: #include "ClientLayerManager.h" michael@0: michael@0: #include "nsUXThemeData.h" michael@0: #include "nsUXThemeConstants.h" michael@0: michael@0: extern "C" { michael@0: #define PIXMAN_DONT_DEFINE_STDINT michael@0: #include "pixman.h" michael@0: } michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::gfx; michael@0: using namespace mozilla::layers; michael@0: using namespace mozilla::widget; michael@0: michael@0: /************************************************************** michael@0: ************************************************************** michael@0: ** michael@0: ** BLOCK: Variables michael@0: ** michael@0: ** nsWindow Class static initializations and global variables. michael@0: ** michael@0: ************************************************************** michael@0: **************************************************************/ michael@0: michael@0: /************************************************************** michael@0: * michael@0: * SECTION: nsWindow statics michael@0: * michael@0: **************************************************************/ michael@0: michael@0: static nsAutoPtr sSharedSurfaceData; michael@0: static gfxIntSize sSharedSurfaceSize; michael@0: michael@0: struct IconMetrics { michael@0: int32_t xMetric; michael@0: int32_t yMetric; michael@0: int32_t defaultSize; michael@0: }; michael@0: michael@0: // Corresponds 1:1 to the IconSizeType enum michael@0: static IconMetrics sIconMetrics[] = { michael@0: {SM_CXSMICON, SM_CYSMICON, 16}, // small icon michael@0: {SM_CXICON, SM_CYICON, 32} // regular icon michael@0: }; michael@0: michael@0: /************************************************************** michael@0: ************************************************************** michael@0: ** michael@0: ** BLOCK: nsWindowGfx impl. michael@0: ** michael@0: ** Misc. graphics related utilities. michael@0: ** michael@0: ************************************************************** michael@0: **************************************************************/ michael@0: michael@0: /* static */ bool michael@0: nsWindow::IsRenderMode(gfxWindowsPlatform::RenderMode rmode) michael@0: { michael@0: return gfxWindowsPlatform::GetPlatform()->GetRenderMode() == rmode; michael@0: } michael@0: michael@0: /************************************************************** michael@0: ************************************************************** michael@0: ** michael@0: ** BLOCK: nsWindow impl. michael@0: ** michael@0: ** Paint related nsWindow methods. michael@0: ** michael@0: ************************************************************** michael@0: **************************************************************/ michael@0: michael@0: // GetRegionToPaint returns the invalidated region that needs to be painted michael@0: nsIntRegion nsWindow::GetRegionToPaint(bool aForceFullRepaint, michael@0: PAINTSTRUCT ps, HDC aDC) michael@0: { michael@0: if (aForceFullRepaint) { michael@0: RECT paintRect; michael@0: ::GetClientRect(mWnd, &paintRect); michael@0: return nsIntRegion(WinUtils::ToIntRect(paintRect)); michael@0: } michael@0: michael@0: HRGN paintRgn = ::CreateRectRgn(0, 0, 0, 0); michael@0: if (paintRgn != nullptr) { michael@0: int result = GetRandomRgn(aDC, paintRgn, SYSRGN); michael@0: if (result == 1) { michael@0: POINT pt = {0,0}; michael@0: ::MapWindowPoints(nullptr, mWnd, &pt, 1); michael@0: ::OffsetRgn(paintRgn, pt.x, pt.y); michael@0: } michael@0: nsIntRegion rgn(WinUtils::ConvertHRGNToRegion(paintRgn)); michael@0: ::DeleteObject(paintRgn); michael@0: return rgn; michael@0: } michael@0: return nsIntRegion(WinUtils::ToIntRect(ps.rcPaint)); michael@0: } michael@0: michael@0: #define WORDSSIZE(x) ((x).width * (x).height) michael@0: static bool michael@0: EnsureSharedSurfaceSize(gfxIntSize size) michael@0: { michael@0: gfxIntSize screenSize; michael@0: screenSize.height = GetSystemMetrics(SM_CYSCREEN); michael@0: screenSize.width = GetSystemMetrics(SM_CXSCREEN); michael@0: michael@0: if (WORDSSIZE(screenSize) > WORDSSIZE(size)) michael@0: size = screenSize; michael@0: michael@0: if (WORDSSIZE(screenSize) < WORDSSIZE(size)) michael@0: NS_WARNING("Trying to create a shared surface larger than the screen"); michael@0: michael@0: if (!sSharedSurfaceData || (WORDSSIZE(size) > WORDSSIZE(sSharedSurfaceSize))) { michael@0: sSharedSurfaceSize = size; michael@0: sSharedSurfaceData = nullptr; michael@0: sSharedSurfaceData = (uint8_t *)malloc(WORDSSIZE(sSharedSurfaceSize) * 4); michael@0: } michael@0: michael@0: return (sSharedSurfaceData != nullptr); michael@0: } michael@0: michael@0: nsIWidgetListener* nsWindow::GetPaintListener() michael@0: { michael@0: if (mDestroyCalled) michael@0: return nullptr; michael@0: return mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener; michael@0: } michael@0: michael@0: bool nsWindow::OnPaint(HDC aDC, uint32_t aNestingLevel) michael@0: { michael@0: // We never have reentrant paint events, except when we're running our RPC michael@0: // windows event spin loop. If we don't trap for this, we'll try to paint, michael@0: // but view manager will refuse to paint the surface, resulting is black michael@0: // flashes on the plugin rendering surface. michael@0: if (mozilla::ipc::MessageChannel::IsSpinLoopActive() && mPainting) michael@0: return false; michael@0: michael@0: if (mWindowType == eWindowType_plugin) { michael@0: michael@0: /** michael@0: * After we CallUpdateWindow to the child, occasionally a WM_PAINT message michael@0: * is posted to the parent event loop with an empty update rect. Do a michael@0: * dummy paint so that Windows stops dispatching WM_PAINT in an inifinite michael@0: * loop. See bug 543788. michael@0: */ michael@0: RECT updateRect; michael@0: if (!GetUpdateRect(mWnd, &updateRect, FALSE) || michael@0: (updateRect.left == updateRect.right && michael@0: updateRect.top == updateRect.bottom)) { michael@0: PAINTSTRUCT ps; michael@0: BeginPaint(mWnd, &ps); michael@0: EndPaint(mWnd, &ps); michael@0: return true; michael@0: } michael@0: michael@0: PluginInstanceParent* instance = reinterpret_cast( michael@0: ::GetPropW(mWnd, L"PluginInstanceParentProperty")); michael@0: if (instance) { michael@0: unused << instance->CallUpdateWindow(); michael@0: } else { michael@0: // We should never get here since in-process plugins should have michael@0: // subclassed our HWND and handled WM_PAINT, but in some cases that michael@0: // could fail. Return without asserting since it's not our fault. michael@0: NS_WARNING("Plugin failed to subclass our window"); michael@0: } michael@0: michael@0: ValidateRect(mWnd, nullptr); michael@0: return true; michael@0: } michael@0: michael@0: ClientLayerManager *clientLayerManager = michael@0: (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) michael@0: ? static_cast(GetLayerManager()) michael@0: : nullptr; michael@0: michael@0: if (clientLayerManager && mCompositorParent && michael@0: !mBounds.IsEqualEdges(mLastPaintBounds)) michael@0: { michael@0: // Do an early async composite so that we at least have something on the michael@0: // screen in the right place, even if the content is out of date. michael@0: mCompositorParent->ScheduleRenderOnCompositorThread(); michael@0: } michael@0: mLastPaintBounds = mBounds; michael@0: michael@0: PAINTSTRUCT ps; michael@0: michael@0: #ifdef MOZ_XUL michael@0: if (!aDC && (eTransparencyTransparent == mTransparencyMode)) michael@0: { michael@0: // For layered translucent windows all drawing should go to memory DC and no michael@0: // WM_PAINT messages are normally generated. To support asynchronous painting michael@0: // we force generation of WM_PAINT messages by invalidating window areas with michael@0: // RedrawWindow, InvalidateRect or InvalidateRgn function calls. michael@0: // BeginPaint/EndPaint must be called to make Windows think that invalid area michael@0: // is painted. Otherwise it will continue sending the same message endlessly. michael@0: ::BeginPaint(mWnd, &ps); michael@0: ::EndPaint(mWnd, &ps); michael@0: michael@0: aDC = mMemoryDC; michael@0: } michael@0: #endif michael@0: michael@0: mPainting = true; michael@0: michael@0: #ifdef WIDGET_DEBUG_OUTPUT michael@0: HRGN debugPaintFlashRegion = nullptr; michael@0: HDC debugPaintFlashDC = nullptr; michael@0: michael@0: if (debug_WantPaintFlashing()) michael@0: { michael@0: debugPaintFlashRegion = ::CreateRectRgn(0, 0, 0, 0); michael@0: ::GetUpdateRgn(mWnd, debugPaintFlashRegion, TRUE); michael@0: debugPaintFlashDC = ::GetDC(mWnd); michael@0: } michael@0: #endif // WIDGET_DEBUG_OUTPUT michael@0: michael@0: HDC hDC = aDC ? aDC : (::BeginPaint(mWnd, &ps)); michael@0: mPaintDC = hDC; michael@0: michael@0: #ifdef MOZ_XUL michael@0: bool forceRepaint = aDC || (eTransparencyTransparent == mTransparencyMode); michael@0: #else michael@0: bool forceRepaint = nullptr != aDC; michael@0: #endif michael@0: nsIntRegion region = GetRegionToPaint(forceRepaint, ps, hDC); michael@0: michael@0: if (clientLayerManager && mCompositorParent) { michael@0: // We need to paint to the screen even if nothing changed, since if we michael@0: // don't have a compositing window manager, our pixels could be stale. michael@0: clientLayerManager->SetNeedsComposite(true); michael@0: clientLayerManager->SendInvalidRegion(region); michael@0: } michael@0: michael@0: nsIWidgetListener* listener = GetPaintListener(); michael@0: if (listener) { michael@0: listener->WillPaintWindow(this); michael@0: } michael@0: // Re-get the listener since the will paint notification may have killed it. michael@0: listener = GetPaintListener(); michael@0: if (!listener) { michael@0: return false; michael@0: } michael@0: michael@0: if (clientLayerManager && mCompositorParent && clientLayerManager->NeedsComposite()) { michael@0: mCompositorParent->ScheduleRenderOnCompositorThread(); michael@0: clientLayerManager->SetNeedsComposite(false); michael@0: } michael@0: michael@0: bool result = true; michael@0: if (!region.IsEmpty() && listener) michael@0: { michael@0: // Should probably pass in a real region here, using GetRandomRgn michael@0: // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/clipping_4q0e.asp michael@0: michael@0: #ifdef WIDGET_DEBUG_OUTPUT michael@0: debug_DumpPaintEvent(stdout, michael@0: this, michael@0: region, michael@0: nsAutoCString("noname"), michael@0: (int32_t) mWnd); michael@0: #endif // WIDGET_DEBUG_OUTPUT michael@0: michael@0: switch (GetLayerManager()->GetBackendType()) { michael@0: case LayersBackend::LAYERS_BASIC: michael@0: { michael@0: nsRefPtr targetSurface; michael@0: michael@0: #if defined(MOZ_XUL) michael@0: // don't support transparency for non-GDI rendering, for now michael@0: if ((IsRenderMode(gfxWindowsPlatform::RENDER_GDI) || michael@0: IsRenderMode(gfxWindowsPlatform::RENDER_DIRECT2D)) && michael@0: eTransparencyTransparent == mTransparencyMode) { michael@0: if (mTransparentSurface == nullptr) michael@0: SetupTranslucentWindowMemoryBitmap(mTransparencyMode); michael@0: targetSurface = mTransparentSurface; michael@0: } michael@0: #endif michael@0: michael@0: nsRefPtr targetSurfaceWin; michael@0: if (!targetSurface && michael@0: (IsRenderMode(gfxWindowsPlatform::RENDER_GDI) || michael@0: IsRenderMode(gfxWindowsPlatform::RENDER_DIRECT2D))) michael@0: { michael@0: uint32_t flags = (mTransparencyMode == eTransparencyOpaque) ? 0 : michael@0: gfxWindowsSurface::FLAG_IS_TRANSPARENT; michael@0: targetSurfaceWin = new gfxWindowsSurface(hDC, flags); michael@0: targetSurface = targetSurfaceWin; michael@0: } michael@0: michael@0: nsRefPtr targetSurfaceImage; michael@0: if (!targetSurface && michael@0: (IsRenderMode(gfxWindowsPlatform::RENDER_IMAGE_STRETCH32) || michael@0: IsRenderMode(gfxWindowsPlatform::RENDER_IMAGE_STRETCH24))) michael@0: { michael@0: gfxIntSize surfaceSize(ps.rcPaint.right - ps.rcPaint.left, michael@0: ps.rcPaint.bottom - ps.rcPaint.top); michael@0: michael@0: if (!EnsureSharedSurfaceSize(surfaceSize)) { michael@0: NS_ERROR("Couldn't allocate a shared image surface!"); michael@0: return false; michael@0: } michael@0: michael@0: // don't use the shared surface directly; instead, create a new one michael@0: // that just reuses its buffer. michael@0: targetSurfaceImage = new gfxImageSurface(sSharedSurfaceData.get(), michael@0: surfaceSize, michael@0: surfaceSize.width * 4, michael@0: gfxImageFormat::RGB24); michael@0: michael@0: if (targetSurfaceImage && !targetSurfaceImage->CairoStatus()) { michael@0: targetSurfaceImage->SetDeviceOffset(gfxPoint(-ps.rcPaint.left, -ps.rcPaint.top)); michael@0: targetSurface = targetSurfaceImage; michael@0: } michael@0: } michael@0: michael@0: if (!targetSurface) { michael@0: NS_ERROR("Invalid RenderMode!"); michael@0: return false; michael@0: } michael@0: michael@0: nsRefPtr thebesContext; michael@0: if (gfxPlatform::GetPlatform()->SupportsAzureContentForType(mozilla::gfx::BackendType::CAIRO)) { michael@0: RECT paintRect; michael@0: ::GetClientRect(mWnd, &paintRect); michael@0: RefPtr dt = michael@0: gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(targetSurface, michael@0: mozilla::gfx::IntSize(paintRect.right - paintRect.left, michael@0: paintRect.bottom - paintRect.top)); michael@0: thebesContext = new gfxContext(dt); michael@0: } else { michael@0: thebesContext = new gfxContext(targetSurface); michael@0: } michael@0: michael@0: // don't need to double buffer with anything but GDI michael@0: BufferMode doubleBuffering = mozilla::layers::BufferMode::BUFFER_NONE; michael@0: if (IsRenderMode(gfxWindowsPlatform::RENDER_GDI) || michael@0: IsRenderMode(gfxWindowsPlatform::RENDER_DIRECT2D)) { michael@0: #ifdef MOZ_XUL michael@0: switch (mTransparencyMode) { michael@0: case eTransparencyGlass: michael@0: case eTransparencyBorderlessGlass: michael@0: default: michael@0: // If we're not doing translucency, then double buffer michael@0: doubleBuffering = mozilla::layers::BufferMode::BUFFERED; michael@0: break; michael@0: case eTransparencyTransparent: michael@0: // If we're rendering with translucency, we're going to be michael@0: // rendering the whole window; make sure we clear it first michael@0: thebesContext->SetOperator(gfxContext::OPERATOR_CLEAR); michael@0: thebesContext->Paint(); michael@0: thebesContext->SetOperator(gfxContext::OPERATOR_OVER); michael@0: break; michael@0: } michael@0: #else michael@0: doubleBuffering = mozilla::layers::BufferMode::BUFFERED; michael@0: #endif michael@0: } michael@0: michael@0: { michael@0: AutoLayerManagerSetup michael@0: setupLayerManager(this, thebesContext, doubleBuffering); michael@0: result = listener->PaintWindow(this, region); michael@0: } michael@0: michael@0: #ifdef MOZ_XUL michael@0: if ((IsRenderMode(gfxWindowsPlatform::RENDER_GDI) || michael@0: IsRenderMode(gfxWindowsPlatform::RENDER_DIRECT2D))&& michael@0: eTransparencyTransparent == mTransparencyMode) { michael@0: // Data from offscreen drawing surface was copied to memory bitmap of transparent michael@0: // bitmap. Now it can be read from memory bitmap to apply alpha channel and after michael@0: // that displayed on the screen. michael@0: UpdateTranslucentWindow(); michael@0: } else michael@0: #endif michael@0: michael@0: if (result) { michael@0: if (IsRenderMode(gfxWindowsPlatform::RENDER_IMAGE_STRETCH24) || michael@0: IsRenderMode(gfxWindowsPlatform::RENDER_IMAGE_STRETCH32)) michael@0: { michael@0: gfxIntSize surfaceSize = targetSurfaceImage->GetSize(); michael@0: michael@0: // Just blit this directly michael@0: BITMAPINFOHEADER bi; michael@0: memset(&bi, 0, sizeof(BITMAPINFOHEADER)); michael@0: bi.biSize = sizeof(BITMAPINFOHEADER); michael@0: bi.biWidth = surfaceSize.width; michael@0: bi.biHeight = - surfaceSize.height; michael@0: bi.biPlanes = 1; michael@0: bi.biBitCount = 32; michael@0: bi.biCompression = BI_RGB; michael@0: michael@0: if (IsRenderMode(gfxWindowsPlatform::RENDER_IMAGE_STRETCH24)) { michael@0: // On Windows CE/Windows Mobile, 24bpp packed-pixel sources michael@0: // seem to be far faster to blit than 32bpp (see bug 484864). michael@0: // So, convert the bits to 24bpp by stripping out the unused michael@0: // alpha byte. 24bpp DIBs also have scanlines that are 4-byte michael@0: // aligned though, so that must be taken into account. michael@0: int srcstride = surfaceSize.width*4; michael@0: int dststride = surfaceSize.width*3; michael@0: dststride = (dststride + 3) & ~3; michael@0: michael@0: // Convert in place michael@0: for (int j = 0; j < surfaceSize.height; ++j) { michael@0: unsigned int *src = (unsigned int*) (targetSurfaceImage->Data() + j*srcstride); michael@0: unsigned int *dst = (unsigned int*) (targetSurfaceImage->Data() + j*dststride); michael@0: michael@0: // go 4 pixels at a time, since each 4 pixels michael@0: // turns into 3 DWORDs when converted into BGR: michael@0: // BGRx BGRx BGRx BGRx -> BGRB GRBG RBGR michael@0: // michael@0: // However, since we're dealing with little-endian ints, this is actually: michael@0: // xRGB xrgb xRGB xrgb -> bRGB GBrg rgbR michael@0: int width_left = surfaceSize.width; michael@0: while (width_left >= 4) { michael@0: unsigned int a = *src++; michael@0: unsigned int b = *src++; michael@0: unsigned int c = *src++; michael@0: unsigned int d = *src++; michael@0: michael@0: *dst++ = (a & 0x00ffffff) | (b << 24); michael@0: *dst++ = ((b & 0x00ffff00) >> 8) | (c << 16); michael@0: *dst++ = ((c & 0x00ff0000) >> 16) | (d << 8); michael@0: michael@0: width_left -= 4; michael@0: } michael@0: michael@0: // then finish up whatever number of pixels are left, michael@0: // using bytes. michael@0: unsigned char *bsrc = (unsigned char*) src; michael@0: unsigned char *bdst = (unsigned char*) dst; michael@0: switch (width_left) { michael@0: case 3: michael@0: *bdst++ = *bsrc++; michael@0: *bdst++ = *bsrc++; michael@0: *bdst++ = *bsrc++; michael@0: bsrc++; michael@0: case 2: michael@0: *bdst++ = *bsrc++; michael@0: *bdst++ = *bsrc++; michael@0: *bdst++ = *bsrc++; michael@0: bsrc++; michael@0: case 1: michael@0: *bdst++ = *bsrc++; michael@0: *bdst++ = *bsrc++; michael@0: *bdst++ = *bsrc++; michael@0: bsrc++; michael@0: case 0: michael@0: break; michael@0: } michael@0: } michael@0: michael@0: bi.biBitCount = 24; michael@0: } michael@0: michael@0: StretchDIBits(hDC, michael@0: ps.rcPaint.left, ps.rcPaint.top, michael@0: surfaceSize.width, surfaceSize.height, michael@0: 0, 0, michael@0: surfaceSize.width, surfaceSize.height, michael@0: targetSurfaceImage->Data(), michael@0: (BITMAPINFO*) &bi, michael@0: DIB_RGB_COLORS, michael@0: SRCCOPY); michael@0: } michael@0: } michael@0: } michael@0: break; michael@0: #ifdef MOZ_ENABLE_D3D9_LAYER michael@0: case LayersBackend::LAYERS_D3D9: michael@0: { michael@0: nsRefPtr layerManagerD3D9 = michael@0: static_cast(GetLayerManager()); michael@0: layerManagerD3D9->SetClippingRegion(region); michael@0: result = listener->PaintWindow(this, region); michael@0: if (layerManagerD3D9->DeviceWasRemoved()) { michael@0: mLayerManager->Destroy(); michael@0: mLayerManager = nullptr; michael@0: // When our device was removed, we should have gfxWindowsPlatform michael@0: // check if its render mode is up to date! michael@0: gfxWindowsPlatform::GetPlatform()->UpdateRenderMode(); michael@0: Invalidate(); michael@0: } michael@0: } michael@0: break; michael@0: #endif michael@0: #ifdef MOZ_ENABLE_D3D10_LAYER michael@0: case LayersBackend::LAYERS_D3D10: michael@0: { michael@0: gfxWindowsPlatform::GetPlatform()->UpdateRenderMode(); michael@0: LayerManagerD3D10 *layerManagerD3D10 = static_cast(GetLayerManager()); michael@0: if (layerManagerD3D10->device() != gfxWindowsPlatform::GetPlatform()->GetD3D10Device()) { michael@0: Invalidate(); michael@0: } else { michael@0: result = listener->PaintWindow(this, region); michael@0: } michael@0: } michael@0: break; michael@0: #endif michael@0: case LayersBackend::LAYERS_CLIENT: michael@0: result = listener->PaintWindow(this, region); michael@0: break; michael@0: default: michael@0: NS_ERROR("Unknown layers backend used!"); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (!aDC) { michael@0: ::EndPaint(mWnd, &ps); michael@0: } michael@0: michael@0: mPaintDC = nullptr; michael@0: mLastPaintEndTime = TimeStamp::Now(); michael@0: michael@0: #if defined(WIDGET_DEBUG_OUTPUT) michael@0: if (debug_WantPaintFlashing()) michael@0: { michael@0: // Only flash paint events which have not ignored the paint message. michael@0: // Those that ignore the paint message aren't painting anything so there michael@0: // is only the overhead of the dispatching the paint event. michael@0: if (result) { michael@0: ::InvertRgn(debugPaintFlashDC, debugPaintFlashRegion); michael@0: PR_Sleep(PR_MillisecondsToInterval(30)); michael@0: ::InvertRgn(debugPaintFlashDC, debugPaintFlashRegion); michael@0: PR_Sleep(PR_MillisecondsToInterval(30)); michael@0: } michael@0: ::ReleaseDC(mWnd, debugPaintFlashDC); michael@0: ::DeleteObject(debugPaintFlashRegion); michael@0: } michael@0: #endif // WIDGET_DEBUG_OUTPUT michael@0: michael@0: mPainting = false; michael@0: michael@0: // Re-get the listener since painting may have killed it. michael@0: listener = GetPaintListener(); michael@0: if (listener) michael@0: listener->DidPaintWindow(); michael@0: michael@0: if (aNestingLevel == 0 && ::GetUpdateRect(mWnd, nullptr, false)) { michael@0: OnPaint(aDC, 1); michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: gfxIntSize nsWindowGfx::GetIconMetrics(IconSizeType aSizeType) { michael@0: int32_t width = ::GetSystemMetrics(sIconMetrics[aSizeType].xMetric); michael@0: int32_t height = ::GetSystemMetrics(sIconMetrics[aSizeType].yMetric); michael@0: michael@0: if (width == 0 || height == 0) { michael@0: width = height = sIconMetrics[aSizeType].defaultSize; michael@0: } michael@0: michael@0: return gfxIntSize(width, height); michael@0: } michael@0: michael@0: nsresult nsWindowGfx::CreateIcon(imgIContainer *aContainer, michael@0: bool aIsCursor, michael@0: uint32_t aHotspotX, michael@0: uint32_t aHotspotY, michael@0: gfxIntSize aScaledSize, michael@0: HICON *aIcon) { michael@0: michael@0: MOZ_ASSERT((aScaledSize.width > 0 && aScaledSize.height > 0) || michael@0: (aScaledSize.width == 0 && aScaledSize.height == 0)); michael@0: michael@0: // Get the image data michael@0: RefPtr surface = michael@0: aContainer->GetFrame(imgIContainer::FRAME_CURRENT, michael@0: imgIContainer::FLAG_SYNC_DECODE); michael@0: NS_ENSURE_TRUE(surface, NS_ERROR_NOT_AVAILABLE); michael@0: michael@0: IntSize frameSize = surface->GetSize(); michael@0: if (frameSize.IsEmpty()) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: IntSize iconSize(aScaledSize.width, aScaledSize.height); michael@0: if (iconSize == IntSize(0, 0)) { // use frame's intrinsic size michael@0: iconSize = frameSize; michael@0: } michael@0: michael@0: RefPtr dataSurface; michael@0: bool mappedOK; michael@0: DataSourceSurface::MappedSurface map; michael@0: michael@0: if (iconSize != frameSize) { michael@0: // Scale the surface michael@0: dataSurface = Factory::CreateDataSourceSurface(iconSize, michael@0: SurfaceFormat::B8G8R8A8); michael@0: NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE); michael@0: mappedOK = dataSurface->Map(DataSourceSurface::MapType::READ_WRITE, &map); michael@0: NS_ENSURE_TRUE(mappedOK, NS_ERROR_FAILURE); michael@0: michael@0: RefPtr dt = michael@0: Factory::CreateDrawTargetForData(BackendType::CAIRO, michael@0: map.mData, michael@0: dataSurface->GetSize(), michael@0: map.mStride, michael@0: SurfaceFormat::B8G8R8A8); michael@0: dt->DrawSurface(surface, michael@0: Rect(0, 0, iconSize.width, iconSize.height), michael@0: Rect(0, 0, frameSize.width, frameSize.height), michael@0: DrawSurfaceOptions(), michael@0: DrawOptions(1.0f, CompositionOp::OP_SOURCE)); michael@0: } else if (surface->GetFormat() != SurfaceFormat::B8G8R8A8) { michael@0: // Convert format to SurfaceFormat::B8G8R8A8 michael@0: dataSurface = gfxUtils:: michael@0: CopySurfaceToDataSourceSurfaceWithFormat(surface, michael@0: SurfaceFormat::B8G8R8A8); michael@0: NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE); michael@0: mappedOK = dataSurface->Map(DataSourceSurface::MapType::READ, &map); michael@0: } else { michael@0: dataSurface = surface->GetDataSurface(); michael@0: NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE); michael@0: mappedOK = dataSurface->Map(DataSourceSurface::MapType::READ, &map); michael@0: } michael@0: NS_ENSURE_TRUE(dataSurface && mappedOK, NS_ERROR_FAILURE); michael@0: MOZ_ASSERT(dataSurface->GetFormat() == SurfaceFormat::B8G8R8A8); michael@0: michael@0: uint8_t* data = nullptr; michael@0: nsAutoArrayPtr autoDeleteArray; michael@0: if (map.mStride == BytesPerPixel(dataSurface->GetFormat()) * iconSize.width) { michael@0: // Mapped data is already packed michael@0: data = map.mData; michael@0: } else { michael@0: // We can't use map.mData since the pixels are not packed (as required by michael@0: // CreateDIBitmap, which is called under the DataToBitmap call below). michael@0: // michael@0: // We must unmap before calling SurfaceToPackedBGRA because it needs access michael@0: // to the pixel data. michael@0: dataSurface->Unmap(); michael@0: map.mData = nullptr; michael@0: michael@0: data = autoDeleteArray = SurfaceToPackedBGRA(dataSurface); michael@0: NS_ENSURE_TRUE(data, NS_ERROR_FAILURE); michael@0: } michael@0: michael@0: HBITMAP bmp = DataToBitmap(data, iconSize.width, -iconSize.height, 32); michael@0: uint8_t* a1data = Data32BitTo1Bit(data, iconSize.width, iconSize.height); michael@0: if (map.mData) { michael@0: dataSurface->Unmap(); michael@0: } michael@0: if (!a1data) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: HBITMAP mbmp = DataToBitmap(a1data, iconSize.width, -iconSize.height, 1); michael@0: PR_Free(a1data); michael@0: michael@0: ICONINFO info = {0}; michael@0: info.fIcon = !aIsCursor; michael@0: info.xHotspot = aHotspotX; michael@0: info.yHotspot = aHotspotY; michael@0: info.hbmMask = mbmp; michael@0: info.hbmColor = bmp; michael@0: michael@0: HCURSOR icon = ::CreateIconIndirect(&info); michael@0: ::DeleteObject(mbmp); michael@0: ::DeleteObject(bmp); michael@0: if (!icon) michael@0: return NS_ERROR_FAILURE; michael@0: *aIcon = icon; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Adjust cursor image data michael@0: uint8_t* nsWindowGfx::Data32BitTo1Bit(uint8_t* aImageData, michael@0: uint32_t aWidth, uint32_t aHeight) michael@0: { michael@0: // We need (aWidth + 7) / 8 bytes plus zero-padding up to a multiple of michael@0: // 4 bytes for each row (HBITMAP requirement). Bug 353553. michael@0: uint32_t outBpr = ((aWidth + 31) / 8) & ~3; michael@0: michael@0: // Allocate and clear mask buffer michael@0: uint8_t* outData = (uint8_t*)PR_Calloc(outBpr, aHeight); michael@0: if (!outData) michael@0: return nullptr; michael@0: michael@0: int32_t *imageRow = (int32_t*)aImageData; michael@0: for (uint32_t curRow = 0; curRow < aHeight; curRow++) { michael@0: uint8_t *outRow = outData + curRow * outBpr; michael@0: uint8_t mask = 0x80; michael@0: for (uint32_t curCol = 0; curCol < aWidth; curCol++) { michael@0: // Use sign bit to test for transparency, as alpha byte is highest byte michael@0: if (*imageRow++ < 0) michael@0: *outRow |= mask; michael@0: michael@0: mask >>= 1; michael@0: if (!mask) { michael@0: outRow ++; michael@0: mask = 0x80; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return outData; michael@0: } michael@0: michael@0: /** michael@0: * Convert the given image data to a HBITMAP. If the requested depth is michael@0: * 32 bit, a bitmap with an alpha channel will be returned. michael@0: * michael@0: * @param aImageData The image data to convert. Must use the format accepted michael@0: * by CreateDIBitmap. michael@0: * @param aWidth With of the bitmap, in pixels. michael@0: * @param aHeight Height of the image, in pixels. michael@0: * @param aDepth Image depth, in bits. Should be one of 1, 24 and 32. michael@0: * michael@0: * @return The HBITMAP representing the image. Caller should call michael@0: * DeleteObject when done with the bitmap. michael@0: * On failure, nullptr will be returned. michael@0: */ michael@0: HBITMAP nsWindowGfx::DataToBitmap(uint8_t* aImageData, michael@0: uint32_t aWidth, michael@0: uint32_t aHeight, michael@0: uint32_t aDepth) michael@0: { michael@0: HDC dc = ::GetDC(nullptr); michael@0: michael@0: if (aDepth == 32) { michael@0: // Alpha channel. We need the new header. michael@0: BITMAPV4HEADER head = { 0 }; michael@0: head.bV4Size = sizeof(head); michael@0: head.bV4Width = aWidth; michael@0: head.bV4Height = aHeight; michael@0: head.bV4Planes = 1; michael@0: head.bV4BitCount = aDepth; michael@0: head.bV4V4Compression = BI_BITFIELDS; michael@0: head.bV4SizeImage = 0; // Uncompressed michael@0: head.bV4XPelsPerMeter = 0; michael@0: head.bV4YPelsPerMeter = 0; michael@0: head.bV4ClrUsed = 0; michael@0: head.bV4ClrImportant = 0; michael@0: michael@0: head.bV4RedMask = 0x00FF0000; michael@0: head.bV4GreenMask = 0x0000FF00; michael@0: head.bV4BlueMask = 0x000000FF; michael@0: head.bV4AlphaMask = 0xFF000000; michael@0: michael@0: HBITMAP bmp = ::CreateDIBitmap(dc, michael@0: reinterpret_cast(&head), michael@0: CBM_INIT, michael@0: aImageData, michael@0: reinterpret_cast(&head), michael@0: DIB_RGB_COLORS); michael@0: ::ReleaseDC(nullptr, dc); michael@0: return bmp; michael@0: } michael@0: michael@0: char reserved_space[sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 2]; michael@0: BITMAPINFOHEADER& head = *(BITMAPINFOHEADER*)reserved_space; michael@0: michael@0: head.biSize = sizeof(BITMAPINFOHEADER); michael@0: head.biWidth = aWidth; michael@0: head.biHeight = aHeight; michael@0: head.biPlanes = 1; michael@0: head.biBitCount = (WORD)aDepth; michael@0: head.biCompression = BI_RGB; michael@0: head.biSizeImage = 0; // Uncompressed michael@0: head.biXPelsPerMeter = 0; michael@0: head.biYPelsPerMeter = 0; michael@0: head.biClrUsed = 0; michael@0: head.biClrImportant = 0; michael@0: michael@0: BITMAPINFO& bi = *(BITMAPINFO*)reserved_space; michael@0: michael@0: if (aDepth == 1) { michael@0: RGBQUAD black = { 0, 0, 0, 0 }; michael@0: RGBQUAD white = { 255, 255, 255, 0 }; michael@0: michael@0: bi.bmiColors[0] = white; michael@0: bi.bmiColors[1] = black; michael@0: } michael@0: michael@0: HBITMAP bmp = ::CreateDIBitmap(dc, &head, CBM_INIT, aImageData, &bi, DIB_RGB_COLORS); michael@0: ::ReleaseDC(nullptr, dc); michael@0: return bmp; michael@0: }