bool nsWindow::OnPaint(HDC aDC, uint32_t aNestingLevel) { // We never have reentrant paint events, except when we're running our RPC // windows event spin loop. If we don't trap for this, we'll try to paint, // but view manager will refuse to paint the surface, resulting is black // flashes on the plugin rendering surface. if (mozilla::ipc::MessageChannel::IsSpinLoopActive() && mPainting) return false; DeviceResetReason resetReason = DeviceResetReason::OK; if (gfxWindowsPlatform::GetPlatform()->DidRenderingDeviceReset(&resetReason)) { gfxCriticalNote << "(nsWindow) Detected device reset: " << (int)resetReason; gfxWindowsPlatform::GetPlatform()->UpdateRenderMode(); uint64_t resetSeqNo = GPUProcessManager::Get()->GetNextDeviceResetSequenceNumber(); nsTArray<nsWindow*> windows = EnumAllWindows(); for (nsWindow* window : windows) { window->OnRenderingDeviceReset(resetSeqNo); } gfxCriticalNote << "(nsWindow) Finished device reset."; return false; } gfxDWriteFont::UpdateClearTypeUsage(); // After we CallUpdateWindow to the child, occasionally a WM_PAINT message // is posted to the parent event loop with an empty update rect. Do a // dummy paint so that Windows stops dispatching WM_PAINT in an inifinite // loop. See bug 543788. if (IsPlugin()) { RECT updateRect; if (!GetUpdateRect(mWnd, &updateRect, FALSE) || (updateRect.left == updateRect.right && updateRect.top == updateRect.bottom)) { PAINTSTRUCT ps; BeginPaint(mWnd, &ps); EndPaint(mWnd, &ps); return true; } if (mWindowType == eWindowType_plugin_ipc_chrome) { // Fire off an async request to the plugin to paint its window mozilla::dom::ContentParent::SendAsyncUpdate(this); ValidateRect(mWnd, nullptr); return true; } PluginInstanceParent* instance = reinterpret_cast<PluginInstanceParent*>( ::GetPropW(mWnd, L"PluginInstanceParentProperty")); if (instance) { Unused << instance->CallUpdateWindow(); } else { // We should never get here since in-process plugins should have // subclassed our HWND and handled WM_PAINT, but in some cases that // could fail. Return without asserting since it's not our fault. NS_WARNING("Plugin failed to subclass our window"); } ValidateRect(mWnd, nullptr); return true; } ClientLayerManager *clientLayerManager = GetLayerManager()->AsClientLayerManager(); if (clientLayerManager && !mBounds.IsEqualEdges(mLastPaintBounds)) { // Do an early async composite so that we at least have something on the // screen in the right place, even if the content is out of date. clientLayerManager->Composite(); } mLastPaintBounds = mBounds; PAINTSTRUCT ps; #ifdef MOZ_XUL if (!aDC && (eTransparencyTransparent == mTransparencyMode)) { // For layered translucent windows all drawing should go to memory DC and no // WM_PAINT messages are normally generated. To support asynchronous painting // we force generation of WM_PAINT messages by invalidating window areas with // RedrawWindow, InvalidateRect or InvalidateRgn function calls. // BeginPaint/EndPaint must be called to make Windows think that invalid area // is painted. Otherwise it will continue sending the same message endlessly. ::BeginPaint(mWnd, &ps); ::EndPaint(mWnd, &ps); // We're guaranteed to have a widget proxy since we called GetLayerManager(). aDC = mCompositorWidgetDelegate->GetTransparentDC(); } #endif mPainting = true; #ifdef WIDGET_DEBUG_OUTPUT HRGN debugPaintFlashRegion = nullptr; HDC debugPaintFlashDC = nullptr; if (debug_WantPaintFlashing()) { debugPaintFlashRegion = ::CreateRectRgn(0, 0, 0, 0); ::GetUpdateRgn(mWnd, debugPaintFlashRegion, TRUE); debugPaintFlashDC = ::GetDC(mWnd); } #endif // WIDGET_DEBUG_OUTPUT HDC hDC = aDC ? aDC : (::BeginPaint(mWnd, &ps)); mPaintDC = hDC; #ifdef MOZ_XUL bool forceRepaint = aDC || (eTransparencyTransparent == mTransparencyMode); #else bool forceRepaint = nullptr != aDC; #endif nsIntRegion region = GetRegionToPaint(forceRepaint, ps, hDC); if (clientLayerManager) { // We need to paint to the screen even if nothing changed, since if we // don't have a compositing window manager, our pixels could be stale. clientLayerManager->SetNeedsComposite(true); clientLayerManager->SendInvalidRegion(region); } RefPtr<nsWindow> strongThis(this); nsIWidgetListener* listener = GetPaintListener(); if (listener) { listener->WillPaintWindow(this); } // Re-get the listener since the will paint notification may have killed it. listener = GetPaintListener(); if (!listener) { return false; } if (clientLayerManager && clientLayerManager->NeedsComposite()) { clientLayerManager->Composite(); clientLayerManager->SetNeedsComposite(false); } bool result = true; if (!region.IsEmpty() && listener) { // Should probably pass in a real region here, using GetRandomRgn // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/clipping_4q0e.asp #ifdef WIDGET_DEBUG_OUTPUT debug_DumpPaintEvent(stdout, this, region, "noname", (int32_t) mWnd); #endif // WIDGET_DEBUG_OUTPUT switch (GetLayerManager()->GetBackendType()) { case LayersBackend::LAYERS_BASIC: { RefPtr<gfxASurface> targetSurface; #if defined(MOZ_XUL) // don't support transparency for non-GDI rendering, for now if (eTransparencyTransparent == mTransparencyMode) { targetSurface = mBasicLayersSurface->EnsureTransparentSurface(); } #endif RefPtr<gfxWindowsSurface> targetSurfaceWin; if (!targetSurface) { uint32_t flags = (mTransparencyMode == eTransparencyOpaque) ? 0 : gfxWindowsSurface::FLAG_IS_TRANSPARENT; targetSurfaceWin = new gfxWindowsSurface(hDC, flags); targetSurface = targetSurfaceWin; } if (!targetSurface) { NS_ERROR("Invalid RenderMode!"); return false; } RECT paintRect; ::GetClientRect(mWnd, &paintRect); RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(targetSurface, IntSize(paintRect.right - paintRect.left, paintRect.bottom - paintRect.top)); if (!dt || !dt->IsValid()) { gfxWarning() << "nsWindow::OnPaint failed in CreateDrawTargetForSurface"; return false; } // don't need to double buffer with anything but GDI BufferMode doubleBuffering = mozilla::layers::BufferMode::BUFFER_NONE; #ifdef MOZ_XUL switch (mTransparencyMode) { case eTransparencyGlass: case eTransparencyBorderlessGlass: default: // If we're not doing translucency, then double buffer doubleBuffering = mozilla::layers::BufferMode::BUFFERED; break; case eTransparencyTransparent: // If we're rendering with translucency, we're going to be // rendering the whole window; make sure we clear it first dt->ClearRect(Rect(0.f, 0.f, dt->GetSize().width, dt->GetSize().height)); break; } #else doubleBuffering = mozilla::layers::BufferMode::BUFFERED; #endif RefPtr<gfxContext> thebesContext = gfxContext::CreateOrNull(dt); MOZ_ASSERT(thebesContext); // already checked draw target above { AutoLayerManagerSetup setupLayerManager(this, thebesContext, doubleBuffering); result = listener->PaintWindow( this, LayoutDeviceIntRegion::FromUnknownRegion(region)); } #ifdef MOZ_XUL if (eTransparencyTransparent == mTransparencyMode) { // 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. mBasicLayersSurface->RedrawTransparentWindow(); } #endif } break; case LayersBackend::LAYERS_CLIENT: { result = listener->PaintWindow( this, LayoutDeviceIntRegion::FromUnknownRegion(region)); if (!gfxEnv::DisableForcePresent() && gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) { nsCOMPtr<nsIRunnable> event = NewRunnableMethod(this, &nsWindow::ForcePresent); NS_DispatchToMainThread(event); } } break; default: NS_ERROR("Unknown layers backend used!"); break; } } if (!aDC) { ::EndPaint(mWnd, &ps); } mPaintDC = nullptr; mLastPaintEndTime = TimeStamp::Now(); #if defined(WIDGET_DEBUG_OUTPUT) if (debug_WantPaintFlashing()) { // Only flash paint events which have not ignored the paint message. // Those that ignore the paint message aren't painting anything so there // is only the overhead of the dispatching the paint event. if (result) { ::InvertRgn(debugPaintFlashDC, debugPaintFlashRegion); PR_Sleep(PR_MillisecondsToInterval(30)); ::InvertRgn(debugPaintFlashDC, debugPaintFlashRegion); PR_Sleep(PR_MillisecondsToInterval(30)); } ::ReleaseDC(mWnd, debugPaintFlashDC); ::DeleteObject(debugPaintFlashRegion); } #endif // WIDGET_DEBUG_OUTPUT mPainting = false; // Re-get the listener since painting may have killed it. listener = GetPaintListener(); if (listener) listener->DidPaintWindow(); if (aNestingLevel == 0 && ::GetUpdateRect(mWnd, nullptr, false)) { OnPaint(aDC, 1); } return result; }
bool nsWindow::OnPaint(HDC aDC, uint32_t aNestingLevel) { // We never have reentrant paint events, except when we're running our RPC // windows event spin loop. If we don't trap for this, we'll try to paint, // but view manager will refuse to paint the surface, resulting is black // flashes on the plugin rendering surface. if (mozilla::ipc::MessageChannel::IsSpinLoopActive() && mPainting) return false; if (gfxWindowsPlatform::GetPlatform()->DidRenderingDeviceReset()) { gfxWindowsPlatform::GetPlatform()->UpdateRenderMode(); EnumAllWindows(ClearCompositor); return false; } // After we CallUpdateWindow to the child, occasionally a WM_PAINT message // is posted to the parent event loop with an empty update rect. Do a // dummy paint so that Windows stops dispatching WM_PAINT in an inifinite // loop. See bug 543788. if (IsPlugin()) { RECT updateRect; if (!GetUpdateRect(mWnd, &updateRect, FALSE) || (updateRect.left == updateRect.right && updateRect.top == updateRect.bottom)) { PAINTSTRUCT ps; BeginPaint(mWnd, &ps); EndPaint(mWnd, &ps); return true; } if (mWindowType == eWindowType_plugin_ipc_chrome) { // Fire off an async request to the plugin to paint its window mozilla::dom::ContentParent::SendAsyncUpdate(this); ValidateRect(mWnd, nullptr); return true; } PluginInstanceParent* instance = reinterpret_cast<PluginInstanceParent*>( ::GetPropW(mWnd, L"PluginInstanceParentProperty")); if (instance) { Unused << instance->CallUpdateWindow(); } else { // We should never get here since in-process plugins should have // subclassed our HWND and handled WM_PAINT, but in some cases that // could fail. Return without asserting since it's not our fault. NS_WARNING("Plugin failed to subclass our window"); } ValidateRect(mWnd, nullptr); return true; } ClientLayerManager *clientLayerManager = (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) ? static_cast<ClientLayerManager*>(GetLayerManager()) : nullptr; if (clientLayerManager && mCompositorParent && !mBounds.IsEqualEdges(mLastPaintBounds)) { // Do an early async composite so that we at least have something on the // screen in the right place, even if the content is out of date. mCompositorParent->ScheduleRenderOnCompositorThread(); } mLastPaintBounds = mBounds; PAINTSTRUCT ps; #ifdef MOZ_XUL if (!aDC && (eTransparencyTransparent == mTransparencyMode)) { // For layered translucent windows all drawing should go to memory DC and no // WM_PAINT messages are normally generated. To support asynchronous painting // we force generation of WM_PAINT messages by invalidating window areas with // RedrawWindow, InvalidateRect or InvalidateRgn function calls. // BeginPaint/EndPaint must be called to make Windows think that invalid area // is painted. Otherwise it will continue sending the same message endlessly. ::BeginPaint(mWnd, &ps); ::EndPaint(mWnd, &ps); aDC = mMemoryDC; } #endif mPainting = true; #ifdef WIDGET_DEBUG_OUTPUT HRGN debugPaintFlashRegion = nullptr; HDC debugPaintFlashDC = nullptr; if (debug_WantPaintFlashing()) { debugPaintFlashRegion = ::CreateRectRgn(0, 0, 0, 0); ::GetUpdateRgn(mWnd, debugPaintFlashRegion, TRUE); debugPaintFlashDC = ::GetDC(mWnd); } #endif // WIDGET_DEBUG_OUTPUT HDC hDC = aDC ? aDC : (::BeginPaint(mWnd, &ps)); mPaintDC = hDC; #ifdef MOZ_XUL bool forceRepaint = aDC || (eTransparencyTransparent == mTransparencyMode); #else bool forceRepaint = nullptr != aDC; #endif nsIntRegion region = GetRegionToPaint(forceRepaint, ps, hDC); if (clientLayerManager && mCompositorParent) { // We need to paint to the screen even if nothing changed, since if we // don't have a compositing window manager, our pixels could be stale. clientLayerManager->SetNeedsComposite(true); clientLayerManager->SendInvalidRegion(region); } nsIWidgetListener* listener = GetPaintListener(); if (listener) { listener->WillPaintWindow(this); } // Re-get the listener since the will paint notification may have killed it. listener = GetPaintListener(); if (!listener) { return false; } if (clientLayerManager && mCompositorParent && clientLayerManager->NeedsComposite()) { mCompositorParent->ScheduleRenderOnCompositorThread(); clientLayerManager->SetNeedsComposite(false); } bool result = true; if (!region.IsEmpty() && listener) { // Should probably pass in a real region here, using GetRandomRgn // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/clipping_4q0e.asp #ifdef WIDGET_DEBUG_OUTPUT debug_DumpPaintEvent(stdout, this, region, nsAutoCString("noname"), (int32_t) mWnd); #endif // WIDGET_DEBUG_OUTPUT switch (GetLayerManager()->GetBackendType()) { case LayersBackend::LAYERS_BASIC: { RefPtr<gfxASurface> targetSurface; #if defined(MOZ_XUL) // don't support transparency for non-GDI rendering, for now if ((IsRenderMode(gfxWindowsPlatform::RENDER_GDI) || IsRenderMode(gfxWindowsPlatform::RENDER_DIRECT2D)) && eTransparencyTransparent == mTransparencyMode) { if (mTransparentSurface == nullptr) SetupTranslucentWindowMemoryBitmap(mTransparencyMode); targetSurface = mTransparentSurface; } #endif RefPtr<gfxWindowsSurface> targetSurfaceWin; if (!targetSurface && (IsRenderMode(gfxWindowsPlatform::RENDER_GDI) || IsRenderMode(gfxWindowsPlatform::RENDER_DIRECT2D))) { uint32_t flags = (mTransparencyMode == eTransparencyOpaque) ? 0 : gfxWindowsSurface::FLAG_IS_TRANSPARENT; targetSurfaceWin = new gfxWindowsSurface(hDC, flags); targetSurface = targetSurfaceWin; } RefPtr<gfxImageSurface> targetSurfaceImage; if (!targetSurface && (IsRenderMode(gfxWindowsPlatform::RENDER_IMAGE_STRETCH32) || IsRenderMode(gfxWindowsPlatform::RENDER_IMAGE_STRETCH24))) { IntSize surfaceSize(ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top); if (!EnsureSharedSurfaceSize(surfaceSize)) { NS_ERROR("Couldn't allocate a shared image surface!"); return false; } // don't use the shared surface directly; instead, create a new one // that just reuses its buffer. targetSurfaceImage = new gfxImageSurface(sSharedSurfaceData.get(), surfaceSize, surfaceSize.width * 4, SurfaceFormat::X8R8G8B8_UINT32); if (targetSurfaceImage && !targetSurfaceImage->CairoStatus()) { targetSurfaceImage->SetDeviceOffset(gfxPoint(-ps.rcPaint.left, -ps.rcPaint.top)); targetSurface = targetSurfaceImage; } } if (!targetSurface) { NS_ERROR("Invalid RenderMode!"); return false; } RECT paintRect; ::GetClientRect(mWnd, &paintRect); RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(targetSurface, IntSize(paintRect.right - paintRect.left, paintRect.bottom - paintRect.top)); if (!dt) { gfxWarning() << "nsWindow::OnPaint failed in CreateDrawTargetForSurface"; return false; } // don't need to double buffer with anything but GDI BufferMode doubleBuffering = mozilla::layers::BufferMode::BUFFER_NONE; if (IsRenderMode(gfxWindowsPlatform::RENDER_GDI) || IsRenderMode(gfxWindowsPlatform::RENDER_DIRECT2D)) { #ifdef MOZ_XUL switch (mTransparencyMode) { case eTransparencyGlass: case eTransparencyBorderlessGlass: default: // If we're not doing translucency, then double buffer doubleBuffering = mozilla::layers::BufferMode::BUFFERED; break; case eTransparencyTransparent: // If we're rendering with translucency, we're going to be // rendering the whole window; make sure we clear it first dt->ClearRect(Rect(0.f, 0.f, dt->GetSize().width, dt->GetSize().height)); break; } #else doubleBuffering = mozilla::layers::BufferMode::BUFFERED; #endif } RefPtr<gfxContext> thebesContext = new gfxContext(dt); { AutoLayerManagerSetup setupLayerManager(this, thebesContext, doubleBuffering); result = listener->PaintWindow( this, LayoutDeviceIntRegion::FromUnknownRegion(region)); } #ifdef MOZ_XUL if ((IsRenderMode(gfxWindowsPlatform::RENDER_GDI) || IsRenderMode(gfxWindowsPlatform::RENDER_DIRECT2D))&& eTransparencyTransparent == mTransparencyMode) { // 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(); } else #endif if (result) { if (IsRenderMode(gfxWindowsPlatform::RENDER_IMAGE_STRETCH24) || IsRenderMode(gfxWindowsPlatform::RENDER_IMAGE_STRETCH32)) { IntSize surfaceSize = targetSurfaceImage->GetSize(); // Just blit this directly BITMAPINFOHEADER bi; memset(&bi, 0, sizeof(BITMAPINFOHEADER)); bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = surfaceSize.width; bi.biHeight = - surfaceSize.height; bi.biPlanes = 1; bi.biBitCount = 32; bi.biCompression = BI_RGB; if (IsRenderMode(gfxWindowsPlatform::RENDER_IMAGE_STRETCH24)) { // On Windows CE/Windows Mobile, 24bpp packed-pixel sources // seem to be far faster to blit than 32bpp (see bug 484864). // So, convert the bits to 24bpp by stripping out the unused // alpha byte. 24bpp DIBs also have scanlines that are 4-byte // aligned though, so that must be taken into account. int srcstride = surfaceSize.width*4; int dststride = surfaceSize.width*3; dststride = (dststride + 3) & ~3; // Convert in place for (int j = 0; j < surfaceSize.height; ++j) { unsigned int *src = (unsigned int*) (targetSurfaceImage->Data() + j*srcstride); unsigned int *dst = (unsigned int*) (targetSurfaceImage->Data() + j*dststride); // go 4 pixels at a time, since each 4 pixels // turns into 3 DWORDs when converted into BGR: // BGRx BGRx BGRx BGRx -> BGRB GRBG RBGR // // However, since we're dealing with little-endian ints, this is actually: // xRGB xrgb xRGB xrgb -> bRGB GBrg rgbR int width_left = surfaceSize.width; while (width_left >= 4) { unsigned int a = *src++; unsigned int b = *src++; unsigned int c = *src++; unsigned int d = *src++; *dst++ = (a & 0x00ffffff) | (b << 24); *dst++ = ((b & 0x00ffff00) >> 8) | (c << 16); *dst++ = ((c & 0x00ff0000) >> 16) | (d << 8); width_left -= 4; } // then finish up whatever number of pixels are left, // using bytes. unsigned char *bsrc = (unsigned char*) src; unsigned char *bdst = (unsigned char*) dst; switch (width_left) { case 3: *bdst++ = *bsrc++; *bdst++ = *bsrc++; *bdst++ = *bsrc++; bsrc++; case 2: *bdst++ = *bsrc++; *bdst++ = *bsrc++; *bdst++ = *bsrc++; bsrc++; case 1: *bdst++ = *bsrc++; *bdst++ = *bsrc++; *bdst++ = *bsrc++; bsrc++; case 0: break; } } bi.biBitCount = 24; } StretchDIBits(hDC, ps.rcPaint.left, ps.rcPaint.top, surfaceSize.width, surfaceSize.height, 0, 0, surfaceSize.width, surfaceSize.height, targetSurfaceImage->Data(), (BITMAPINFO*) &bi, DIB_RGB_COLORS, SRCCOPY); } } } break; case LayersBackend::LAYERS_CLIENT: result = listener->PaintWindow( this, LayoutDeviceIntRegion::FromUnknownRegion(region)); break; default: NS_ERROR("Unknown layers backend used!"); break; }