widget/windows/nsWindowGfx.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 /*
     7  * nsWindowGfx - Painting and aceleration.
     8  */
    10 // XXX Future: this should really be a stand alone class stored as
    11 // a member of nsWindow with getters and setters for things like render
    12 // mode and methods for handling paint.
    14 /**************************************************************
    15  **************************************************************
    16  **
    17  ** BLOCK: Includes
    18  **
    19  ** Include headers.
    20  **
    21  **************************************************************
    22  **************************************************************/
    24 #include "mozilla/plugins/PluginInstanceParent.h"
    25 using mozilla::plugins::PluginInstanceParent;
    27 #include "nsWindowGfx.h"
    28 #include <windows.h>
    29 #include "gfxImageSurface.h"
    30 #include "gfxUtils.h"
    31 #include "gfxWindowsSurface.h"
    32 #include "gfxWindowsPlatform.h"
    33 #include "mozilla/gfx/2D.h"
    34 #include "mozilla/gfx/DataSurfaceHelpers.h"
    35 #include "mozilla/gfx/Tools.h"
    36 #include "mozilla/RefPtr.h"
    37 #include "nsGfxCIID.h"
    38 #include "gfxContext.h"
    39 #include "nsRenderingContext.h"
    40 #include "prmem.h"
    41 #include "WinUtils.h"
    42 #include "nsIWidgetListener.h"
    43 #include "mozilla/unused.h"
    45 #ifdef MOZ_ENABLE_D3D9_LAYER
    46 #include "LayerManagerD3D9.h"
    47 #endif
    48 #ifdef MOZ_ENABLE_D3D10_LAYER
    49 #include "LayerManagerD3D10.h"
    50 #endif
    51 #include "mozilla/layers/CompositorParent.h"
    52 #include "ClientLayerManager.h"
    54 #include "nsUXThemeData.h"
    55 #include "nsUXThemeConstants.h"
    57 extern "C" {
    58 #define PIXMAN_DONT_DEFINE_STDINT
    59 #include "pixman.h"
    60 }
    62 using namespace mozilla;
    63 using namespace mozilla::gfx;
    64 using namespace mozilla::layers;
    65 using namespace mozilla::widget;
    67 /**************************************************************
    68  **************************************************************
    69  **
    70  ** BLOCK: Variables
    71  **
    72  ** nsWindow Class static initializations and global variables.
    73  **
    74  **************************************************************
    75  **************************************************************/
    77 /**************************************************************
    78  *
    79  * SECTION: nsWindow statics
    80  *
    81  **************************************************************/
    83 static nsAutoPtr<uint8_t>  sSharedSurfaceData;
    84 static gfxIntSize          sSharedSurfaceSize;
    86 struct IconMetrics {
    87   int32_t xMetric;
    88   int32_t yMetric;
    89   int32_t defaultSize;
    90 };
    92 // Corresponds 1:1 to the IconSizeType enum
    93 static IconMetrics sIconMetrics[] = {
    94   {SM_CXSMICON, SM_CYSMICON, 16}, // small icon
    95   {SM_CXICON,   SM_CYICON,   32}  // regular icon
    96 };
    98 /**************************************************************
    99  **************************************************************
   100  **
   101  ** BLOCK: nsWindowGfx impl.
   102  **
   103  ** Misc. graphics related utilities.
   104  **
   105  **************************************************************
   106  **************************************************************/
   108 /* static */ bool
   109 nsWindow::IsRenderMode(gfxWindowsPlatform::RenderMode rmode)
   110 {
   111   return gfxWindowsPlatform::GetPlatform()->GetRenderMode() == rmode;
   112 }
   114 /**************************************************************
   115  **************************************************************
   116  **
   117  ** BLOCK: nsWindow impl.
   118  **
   119  ** Paint related nsWindow methods.
   120  **
   121  **************************************************************
   122  **************************************************************/
   124 // GetRegionToPaint returns the invalidated region that needs to be painted
   125 nsIntRegion nsWindow::GetRegionToPaint(bool aForceFullRepaint,
   126                                        PAINTSTRUCT ps, HDC aDC)
   127 {
   128   if (aForceFullRepaint) {
   129     RECT paintRect;
   130     ::GetClientRect(mWnd, &paintRect);
   131     return nsIntRegion(WinUtils::ToIntRect(paintRect));
   132   }
   134   HRGN paintRgn = ::CreateRectRgn(0, 0, 0, 0);
   135   if (paintRgn != nullptr) {
   136     int result = GetRandomRgn(aDC, paintRgn, SYSRGN);
   137     if (result == 1) {
   138       POINT pt = {0,0};
   139       ::MapWindowPoints(nullptr, mWnd, &pt, 1);
   140       ::OffsetRgn(paintRgn, pt.x, pt.y);
   141     }
   142     nsIntRegion rgn(WinUtils::ConvertHRGNToRegion(paintRgn));
   143     ::DeleteObject(paintRgn);
   144     return rgn;
   145   }
   146   return nsIntRegion(WinUtils::ToIntRect(ps.rcPaint));
   147 }
   149 #define WORDSSIZE(x) ((x).width * (x).height)
   150 static bool
   151 EnsureSharedSurfaceSize(gfxIntSize size)
   152 {
   153   gfxIntSize screenSize;
   154   screenSize.height = GetSystemMetrics(SM_CYSCREEN);
   155   screenSize.width = GetSystemMetrics(SM_CXSCREEN);
   157   if (WORDSSIZE(screenSize) > WORDSSIZE(size))
   158     size = screenSize;
   160   if (WORDSSIZE(screenSize) < WORDSSIZE(size))
   161     NS_WARNING("Trying to create a shared surface larger than the screen");
   163   if (!sSharedSurfaceData || (WORDSSIZE(size) > WORDSSIZE(sSharedSurfaceSize))) {
   164     sSharedSurfaceSize = size;
   165     sSharedSurfaceData = nullptr;
   166     sSharedSurfaceData = (uint8_t *)malloc(WORDSSIZE(sSharedSurfaceSize) * 4);
   167   }
   169   return (sSharedSurfaceData != nullptr);
   170 }
   172 nsIWidgetListener* nsWindow::GetPaintListener()
   173 {
   174   if (mDestroyCalled)
   175     return nullptr;
   176   return mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener;
   177 }
   179 bool nsWindow::OnPaint(HDC aDC, uint32_t aNestingLevel)
   180 {
   181   // We never have reentrant paint events, except when we're running our RPC
   182   // windows event spin loop. If we don't trap for this, we'll try to paint,
   183   // but view manager will refuse to paint the surface, resulting is black
   184   // flashes on the plugin rendering surface.
   185   if (mozilla::ipc::MessageChannel::IsSpinLoopActive() && mPainting)
   186     return false;
   188   if (mWindowType == eWindowType_plugin) {
   190     /**
   191      * After we CallUpdateWindow to the child, occasionally a WM_PAINT message
   192      * is posted to the parent event loop with an empty update rect. Do a
   193      * dummy paint so that Windows stops dispatching WM_PAINT in an inifinite
   194      * loop. See bug 543788.
   195      */
   196     RECT updateRect;
   197     if (!GetUpdateRect(mWnd, &updateRect, FALSE) ||
   198         (updateRect.left == updateRect.right &&
   199          updateRect.top == updateRect.bottom)) {
   200       PAINTSTRUCT ps;
   201       BeginPaint(mWnd, &ps);
   202       EndPaint(mWnd, &ps);
   203       return true;
   204     }
   206     PluginInstanceParent* instance = reinterpret_cast<PluginInstanceParent*>(
   207       ::GetPropW(mWnd, L"PluginInstanceParentProperty"));
   208     if (instance) {
   209       unused << instance->CallUpdateWindow();
   210     } else {
   211       // We should never get here since in-process plugins should have
   212       // subclassed our HWND and handled WM_PAINT, but in some cases that
   213       // could fail. Return without asserting since it's not our fault.
   214       NS_WARNING("Plugin failed to subclass our window");
   215     }
   217     ValidateRect(mWnd, nullptr);
   218     return true;
   219   }
   221   ClientLayerManager *clientLayerManager =
   222       (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT)
   223       ? static_cast<ClientLayerManager*>(GetLayerManager())
   224       : nullptr;
   226   if (clientLayerManager && mCompositorParent &&
   227       !mBounds.IsEqualEdges(mLastPaintBounds))
   228   {
   229     // Do an early async composite so that we at least have something on the
   230     // screen in the right place, even if the content is out of date.
   231     mCompositorParent->ScheduleRenderOnCompositorThread();
   232   }
   233   mLastPaintBounds = mBounds;
   235   PAINTSTRUCT ps;
   237 #ifdef MOZ_XUL
   238   if (!aDC && (eTransparencyTransparent == mTransparencyMode))
   239   {
   240     // For layered translucent windows all drawing should go to memory DC and no
   241     // WM_PAINT messages are normally generated. To support asynchronous painting
   242     // we force generation of WM_PAINT messages by invalidating window areas with
   243     // RedrawWindow, InvalidateRect or InvalidateRgn function calls.
   244     // BeginPaint/EndPaint must be called to make Windows think that invalid area
   245     // is painted. Otherwise it will continue sending the same message endlessly.
   246     ::BeginPaint(mWnd, &ps);
   247     ::EndPaint(mWnd, &ps);
   249     aDC = mMemoryDC;
   250   }
   251 #endif
   253   mPainting = true;
   255 #ifdef WIDGET_DEBUG_OUTPUT
   256   HRGN debugPaintFlashRegion = nullptr;
   257   HDC debugPaintFlashDC = nullptr;
   259   if (debug_WantPaintFlashing())
   260   {
   261     debugPaintFlashRegion = ::CreateRectRgn(0, 0, 0, 0);
   262     ::GetUpdateRgn(mWnd, debugPaintFlashRegion, TRUE);
   263     debugPaintFlashDC = ::GetDC(mWnd);
   264   }
   265 #endif // WIDGET_DEBUG_OUTPUT
   267   HDC hDC = aDC ? aDC : (::BeginPaint(mWnd, &ps));
   268   mPaintDC = hDC;
   270 #ifdef MOZ_XUL
   271   bool forceRepaint = aDC || (eTransparencyTransparent == mTransparencyMode);
   272 #else
   273   bool forceRepaint = nullptr != aDC;
   274 #endif
   275   nsIntRegion region = GetRegionToPaint(forceRepaint, ps, hDC);
   277   if (clientLayerManager && mCompositorParent) {
   278     // We need to paint to the screen even if nothing changed, since if we
   279     // don't have a compositing window manager, our pixels could be stale.
   280     clientLayerManager->SetNeedsComposite(true);
   281     clientLayerManager->SendInvalidRegion(region);
   282   }
   284   nsIWidgetListener* listener = GetPaintListener();
   285   if (listener) {
   286     listener->WillPaintWindow(this);
   287   }
   288   // Re-get the listener since the will paint notification may have killed it.
   289   listener = GetPaintListener();
   290   if (!listener) {
   291     return false;
   292   }
   294   if (clientLayerManager && mCompositorParent && clientLayerManager->NeedsComposite()) {
   295     mCompositorParent->ScheduleRenderOnCompositorThread();
   296     clientLayerManager->SetNeedsComposite(false);
   297   }
   299   bool result = true;
   300   if (!region.IsEmpty() && listener)
   301   {
   302     // Should probably pass in a real region here, using GetRandomRgn
   303     // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/clipping_4q0e.asp
   305 #ifdef WIDGET_DEBUG_OUTPUT
   306     debug_DumpPaintEvent(stdout,
   307                          this,
   308                          region,
   309                          nsAutoCString("noname"),
   310                          (int32_t) mWnd);
   311 #endif // WIDGET_DEBUG_OUTPUT
   313     switch (GetLayerManager()->GetBackendType()) {
   314       case LayersBackend::LAYERS_BASIC:
   315         {
   316           nsRefPtr<gfxASurface> targetSurface;
   318 #if defined(MOZ_XUL)
   319           // don't support transparency for non-GDI rendering, for now
   320           if ((IsRenderMode(gfxWindowsPlatform::RENDER_GDI) ||
   321                IsRenderMode(gfxWindowsPlatform::RENDER_DIRECT2D)) &&
   322               eTransparencyTransparent == mTransparencyMode) {
   323             if (mTransparentSurface == nullptr)
   324               SetupTranslucentWindowMemoryBitmap(mTransparencyMode);
   325             targetSurface = mTransparentSurface;
   326           }
   327 #endif
   329           nsRefPtr<gfxWindowsSurface> targetSurfaceWin;
   330           if (!targetSurface &&
   331               (IsRenderMode(gfxWindowsPlatform::RENDER_GDI) ||
   332                IsRenderMode(gfxWindowsPlatform::RENDER_DIRECT2D)))
   333           {
   334             uint32_t flags = (mTransparencyMode == eTransparencyOpaque) ? 0 :
   335                 gfxWindowsSurface::FLAG_IS_TRANSPARENT;
   336             targetSurfaceWin = new gfxWindowsSurface(hDC, flags);
   337             targetSurface = targetSurfaceWin;
   338           }
   340           nsRefPtr<gfxImageSurface> targetSurfaceImage;
   341           if (!targetSurface &&
   342               (IsRenderMode(gfxWindowsPlatform::RENDER_IMAGE_STRETCH32) ||
   343                IsRenderMode(gfxWindowsPlatform::RENDER_IMAGE_STRETCH24)))
   344           {
   345             gfxIntSize surfaceSize(ps.rcPaint.right - ps.rcPaint.left,
   346                                    ps.rcPaint.bottom - ps.rcPaint.top);
   348             if (!EnsureSharedSurfaceSize(surfaceSize)) {
   349               NS_ERROR("Couldn't allocate a shared image surface!");
   350               return false;
   351             }
   353             // don't use the shared surface directly; instead, create a new one
   354             // that just reuses its buffer.
   355             targetSurfaceImage = new gfxImageSurface(sSharedSurfaceData.get(),
   356                                                      surfaceSize,
   357                                                      surfaceSize.width * 4,
   358                                                      gfxImageFormat::RGB24);
   360             if (targetSurfaceImage && !targetSurfaceImage->CairoStatus()) {
   361               targetSurfaceImage->SetDeviceOffset(gfxPoint(-ps.rcPaint.left, -ps.rcPaint.top));
   362               targetSurface = targetSurfaceImage;
   363             }
   364           }
   366           if (!targetSurface) {
   367             NS_ERROR("Invalid RenderMode!");
   368             return false;
   369           }
   371           nsRefPtr<gfxContext> thebesContext;
   372           if (gfxPlatform::GetPlatform()->SupportsAzureContentForType(mozilla::gfx::BackendType::CAIRO)) {
   373             RECT paintRect;
   374             ::GetClientRect(mWnd, &paintRect);
   375             RefPtr<mozilla::gfx::DrawTarget> dt =
   376               gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(targetSurface,
   377                                                                      mozilla::gfx::IntSize(paintRect.right - paintRect.left,
   378                                                                                            paintRect.bottom - paintRect.top));
   379             thebesContext = new gfxContext(dt);
   380           } else {
   381             thebesContext = new gfxContext(targetSurface);
   382           }
   384           // don't need to double buffer with anything but GDI
   385           BufferMode doubleBuffering = mozilla::layers::BufferMode::BUFFER_NONE;
   386           if (IsRenderMode(gfxWindowsPlatform::RENDER_GDI) ||
   387               IsRenderMode(gfxWindowsPlatform::RENDER_DIRECT2D)) {
   388 #ifdef MOZ_XUL
   389             switch (mTransparencyMode) {
   390               case eTransparencyGlass:
   391               case eTransparencyBorderlessGlass:
   392               default:
   393                 // If we're not doing translucency, then double buffer
   394                 doubleBuffering = mozilla::layers::BufferMode::BUFFERED;
   395                 break;
   396               case eTransparencyTransparent:
   397                 // If we're rendering with translucency, we're going to be
   398                 // rendering the whole window; make sure we clear it first
   399                 thebesContext->SetOperator(gfxContext::OPERATOR_CLEAR);
   400                 thebesContext->Paint();
   401                 thebesContext->SetOperator(gfxContext::OPERATOR_OVER);
   402                 break;
   403             }
   404 #else
   405             doubleBuffering = mozilla::layers::BufferMode::BUFFERED;
   406 #endif
   407           }
   409           {
   410             AutoLayerManagerSetup
   411                 setupLayerManager(this, thebesContext, doubleBuffering);
   412             result = listener->PaintWindow(this, region);
   413           }
   415 #ifdef MOZ_XUL
   416           if ((IsRenderMode(gfxWindowsPlatform::RENDER_GDI) ||
   417                IsRenderMode(gfxWindowsPlatform::RENDER_DIRECT2D))&&
   418               eTransparencyTransparent == mTransparencyMode) {
   419             // Data from offscreen drawing surface was copied to memory bitmap of transparent
   420             // bitmap. Now it can be read from memory bitmap to apply alpha channel and after
   421             // that displayed on the screen.
   422             UpdateTranslucentWindow();
   423           } else
   424 #endif
   426           if (result) {
   427             if (IsRenderMode(gfxWindowsPlatform::RENDER_IMAGE_STRETCH24) ||
   428                 IsRenderMode(gfxWindowsPlatform::RENDER_IMAGE_STRETCH32))
   429             {
   430               gfxIntSize surfaceSize = targetSurfaceImage->GetSize();
   432               // Just blit this directly
   433               BITMAPINFOHEADER bi;
   434               memset(&bi, 0, sizeof(BITMAPINFOHEADER));
   435               bi.biSize = sizeof(BITMAPINFOHEADER);
   436               bi.biWidth = surfaceSize.width;
   437               bi.biHeight = - surfaceSize.height;
   438               bi.biPlanes = 1;
   439               bi.biBitCount = 32;
   440               bi.biCompression = BI_RGB;
   442               if (IsRenderMode(gfxWindowsPlatform::RENDER_IMAGE_STRETCH24)) {
   443                 // On Windows CE/Windows Mobile, 24bpp packed-pixel sources
   444                 // seem to be far faster to blit than 32bpp (see bug 484864).
   445                 // So, convert the bits to 24bpp by stripping out the unused
   446                 // alpha byte.  24bpp DIBs also have scanlines that are 4-byte
   447                 // aligned though, so that must be taken into account.
   448                 int srcstride = surfaceSize.width*4;
   449                 int dststride = surfaceSize.width*3;
   450                 dststride = (dststride + 3) & ~3;
   452                 // Convert in place
   453                 for (int j = 0; j < surfaceSize.height; ++j) {
   454                   unsigned int *src = (unsigned int*) (targetSurfaceImage->Data() + j*srcstride);
   455                   unsigned int *dst = (unsigned int*) (targetSurfaceImage->Data() + j*dststride);
   457                   // go 4 pixels at a time, since each 4 pixels
   458                   // turns into 3 DWORDs when converted into BGR:
   459                   // BGRx BGRx BGRx BGRx -> BGRB GRBG RBGR
   460                   //
   461                   // However, since we're dealing with little-endian ints, this is actually:
   462                   // xRGB xrgb xRGB xrgb -> bRGB GBrg rgbR
   463                   int width_left = surfaceSize.width;
   464                   while (width_left >= 4) {
   465                     unsigned int a = *src++;
   466                     unsigned int b = *src++;
   467                     unsigned int c = *src++;
   468                     unsigned int d = *src++;
   470                     *dst++ =  (a & 0x00ffffff)        | (b << 24);
   471                     *dst++ = ((b & 0x00ffff00) >> 8)  | (c << 16);
   472                     *dst++ = ((c & 0x00ff0000) >> 16) | (d << 8);
   474                     width_left -= 4;
   475                   }
   477                   // then finish up whatever number of pixels are left,
   478                   // using bytes.
   479                   unsigned char *bsrc = (unsigned char*) src;
   480                   unsigned char *bdst = (unsigned char*) dst;
   481                   switch (width_left) {
   482                     case 3:
   483                       *bdst++ = *bsrc++;
   484                       *bdst++ = *bsrc++;
   485                       *bdst++ = *bsrc++;
   486                       bsrc++;
   487                     case 2:
   488                       *bdst++ = *bsrc++;
   489                       *bdst++ = *bsrc++;
   490                       *bdst++ = *bsrc++;
   491                       bsrc++;
   492                     case 1:
   493                       *bdst++ = *bsrc++;
   494                       *bdst++ = *bsrc++;
   495                       *bdst++ = *bsrc++;
   496                       bsrc++;
   497                     case 0:
   498                       break;
   499                   }
   500                 }
   502                 bi.biBitCount = 24;
   503               }
   505               StretchDIBits(hDC,
   506                             ps.rcPaint.left, ps.rcPaint.top,
   507                             surfaceSize.width, surfaceSize.height,
   508                             0, 0,
   509                             surfaceSize.width, surfaceSize.height,
   510                             targetSurfaceImage->Data(),
   511                             (BITMAPINFO*) &bi,
   512                             DIB_RGB_COLORS,
   513                             SRCCOPY);
   514             }
   515           }
   516         }
   517         break;
   518 #ifdef MOZ_ENABLE_D3D9_LAYER
   519       case LayersBackend::LAYERS_D3D9:
   520         {
   521           nsRefPtr<LayerManagerD3D9> layerManagerD3D9 =
   522             static_cast<mozilla::layers::LayerManagerD3D9*>(GetLayerManager());
   523           layerManagerD3D9->SetClippingRegion(region);
   524           result = listener->PaintWindow(this, region);
   525           if (layerManagerD3D9->DeviceWasRemoved()) {
   526             mLayerManager->Destroy();
   527             mLayerManager = nullptr;
   528             // When our device was removed, we should have gfxWindowsPlatform
   529             // check if its render mode is up to date!
   530             gfxWindowsPlatform::GetPlatform()->UpdateRenderMode();
   531             Invalidate();
   532           }
   533         }
   534         break;
   535 #endif
   536 #ifdef MOZ_ENABLE_D3D10_LAYER
   537       case LayersBackend::LAYERS_D3D10:
   538         {
   539           gfxWindowsPlatform::GetPlatform()->UpdateRenderMode();
   540           LayerManagerD3D10 *layerManagerD3D10 = static_cast<mozilla::layers::LayerManagerD3D10*>(GetLayerManager());
   541           if (layerManagerD3D10->device() != gfxWindowsPlatform::GetPlatform()->GetD3D10Device()) {
   542             Invalidate();
   543           } else {
   544             result = listener->PaintWindow(this, region);
   545           }
   546         }
   547         break;
   548 #endif
   549       case LayersBackend::LAYERS_CLIENT:
   550         result = listener->PaintWindow(this, region);
   551         break;
   552       default:
   553         NS_ERROR("Unknown layers backend used!");
   554         break;
   555     }
   556   }
   558   if (!aDC) {
   559     ::EndPaint(mWnd, &ps);
   560   }
   562   mPaintDC = nullptr;
   563   mLastPaintEndTime = TimeStamp::Now();
   565 #if defined(WIDGET_DEBUG_OUTPUT)
   566   if (debug_WantPaintFlashing())
   567   {
   568     // Only flash paint events which have not ignored the paint message.
   569     // Those that ignore the paint message aren't painting anything so there
   570     // is only the overhead of the dispatching the paint event.
   571     if (result) {
   572       ::InvertRgn(debugPaintFlashDC, debugPaintFlashRegion);
   573       PR_Sleep(PR_MillisecondsToInterval(30));
   574       ::InvertRgn(debugPaintFlashDC, debugPaintFlashRegion);
   575       PR_Sleep(PR_MillisecondsToInterval(30));
   576     }
   577     ::ReleaseDC(mWnd, debugPaintFlashDC);
   578     ::DeleteObject(debugPaintFlashRegion);
   579   }
   580 #endif // WIDGET_DEBUG_OUTPUT
   582   mPainting = false;
   584   // Re-get the listener since painting may have killed it.
   585   listener = GetPaintListener();
   586   if (listener)
   587     listener->DidPaintWindow();
   589   if (aNestingLevel == 0 && ::GetUpdateRect(mWnd, nullptr, false)) {
   590     OnPaint(aDC, 1);
   591   }
   593   return result;
   594 }
   596 gfxIntSize nsWindowGfx::GetIconMetrics(IconSizeType aSizeType) {
   597   int32_t width = ::GetSystemMetrics(sIconMetrics[aSizeType].xMetric);
   598   int32_t height = ::GetSystemMetrics(sIconMetrics[aSizeType].yMetric);
   600   if (width == 0 || height == 0) {
   601     width = height = sIconMetrics[aSizeType].defaultSize;
   602   }
   604   return gfxIntSize(width, height);
   605 }
   607 nsresult nsWindowGfx::CreateIcon(imgIContainer *aContainer,
   608                                   bool aIsCursor,
   609                                   uint32_t aHotspotX,
   610                                   uint32_t aHotspotY,
   611                                   gfxIntSize aScaledSize,
   612                                   HICON *aIcon) {
   614   MOZ_ASSERT((aScaledSize.width > 0 && aScaledSize.height > 0) ||
   615              (aScaledSize.width == 0 && aScaledSize.height == 0));
   617   // Get the image data
   618   RefPtr<SourceSurface> surface =
   619     aContainer->GetFrame(imgIContainer::FRAME_CURRENT,
   620                          imgIContainer::FLAG_SYNC_DECODE);
   621   NS_ENSURE_TRUE(surface, NS_ERROR_NOT_AVAILABLE);
   623   IntSize frameSize = surface->GetSize();
   624   if (frameSize.IsEmpty()) {
   625     return NS_ERROR_FAILURE;
   626   }
   628   IntSize iconSize(aScaledSize.width, aScaledSize.height);
   629   if (iconSize == IntSize(0, 0)) { // use frame's intrinsic size
   630     iconSize = frameSize;
   631   }
   633   RefPtr<DataSourceSurface> dataSurface;
   634   bool mappedOK;
   635   DataSourceSurface::MappedSurface map;
   637   if (iconSize != frameSize) {
   638     // Scale the surface
   639     dataSurface = Factory::CreateDataSourceSurface(iconSize,
   640                                                    SurfaceFormat::B8G8R8A8);
   641     NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
   642     mappedOK = dataSurface->Map(DataSourceSurface::MapType::READ_WRITE, &map);
   643     NS_ENSURE_TRUE(mappedOK, NS_ERROR_FAILURE);
   645     RefPtr<DrawTarget> dt =
   646       Factory::CreateDrawTargetForData(BackendType::CAIRO,
   647                                        map.mData,
   648                                        dataSurface->GetSize(),
   649                                        map.mStride,
   650                                        SurfaceFormat::B8G8R8A8);
   651     dt->DrawSurface(surface,
   652                     Rect(0, 0, iconSize.width, iconSize.height),
   653                     Rect(0, 0, frameSize.width, frameSize.height),
   654                     DrawSurfaceOptions(),
   655                     DrawOptions(1.0f, CompositionOp::OP_SOURCE));
   656   } else if (surface->GetFormat() != SurfaceFormat::B8G8R8A8) {
   657     // Convert format to SurfaceFormat::B8G8R8A8
   658     dataSurface = gfxUtils::
   659       CopySurfaceToDataSourceSurfaceWithFormat(surface,
   660                                                SurfaceFormat::B8G8R8A8);
   661     NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
   662     mappedOK = dataSurface->Map(DataSourceSurface::MapType::READ, &map);
   663   } else {
   664     dataSurface = surface->GetDataSurface();
   665     NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
   666     mappedOK = dataSurface->Map(DataSourceSurface::MapType::READ, &map);
   667   }
   668   NS_ENSURE_TRUE(dataSurface && mappedOK, NS_ERROR_FAILURE);
   669   MOZ_ASSERT(dataSurface->GetFormat() == SurfaceFormat::B8G8R8A8);
   671   uint8_t* data = nullptr;
   672   nsAutoArrayPtr<uint8_t> autoDeleteArray;
   673   if (map.mStride == BytesPerPixel(dataSurface->GetFormat()) * iconSize.width) {
   674     // Mapped data is already packed
   675     data = map.mData;
   676   } else {
   677     // We can't use map.mData since the pixels are not packed (as required by
   678     // CreateDIBitmap, which is called under the DataToBitmap call below).
   679     //
   680     // We must unmap before calling SurfaceToPackedBGRA because it needs access
   681     // to the pixel data.
   682     dataSurface->Unmap();
   683     map.mData = nullptr;
   685     data = autoDeleteArray = SurfaceToPackedBGRA(dataSurface);
   686     NS_ENSURE_TRUE(data, NS_ERROR_FAILURE);
   687   }
   689   HBITMAP bmp = DataToBitmap(data, iconSize.width, -iconSize.height, 32);
   690   uint8_t* a1data = Data32BitTo1Bit(data, iconSize.width, iconSize.height);
   691   if (map.mData) {
   692     dataSurface->Unmap();
   693   }
   694   if (!a1data) {
   695     return NS_ERROR_FAILURE;
   696   }
   698   HBITMAP mbmp = DataToBitmap(a1data, iconSize.width, -iconSize.height, 1);
   699   PR_Free(a1data);
   701   ICONINFO info = {0};
   702   info.fIcon = !aIsCursor;
   703   info.xHotspot = aHotspotX;
   704   info.yHotspot = aHotspotY;
   705   info.hbmMask = mbmp;
   706   info.hbmColor = bmp;
   708   HCURSOR icon = ::CreateIconIndirect(&info);
   709   ::DeleteObject(mbmp);
   710   ::DeleteObject(bmp);
   711   if (!icon)
   712     return NS_ERROR_FAILURE;
   713   *aIcon = icon;
   714   return NS_OK;
   715 }
   717 // Adjust cursor image data
   718 uint8_t* nsWindowGfx::Data32BitTo1Bit(uint8_t* aImageData,
   719                                       uint32_t aWidth, uint32_t aHeight)
   720 {
   721   // We need (aWidth + 7) / 8 bytes plus zero-padding up to a multiple of
   722   // 4 bytes for each row (HBITMAP requirement). Bug 353553.
   723   uint32_t outBpr = ((aWidth + 31) / 8) & ~3;
   725   // Allocate and clear mask buffer
   726   uint8_t* outData = (uint8_t*)PR_Calloc(outBpr, aHeight);
   727   if (!outData)
   728     return nullptr;
   730   int32_t *imageRow = (int32_t*)aImageData;
   731   for (uint32_t curRow = 0; curRow < aHeight; curRow++) {
   732     uint8_t *outRow = outData + curRow * outBpr;
   733     uint8_t mask = 0x80;
   734     for (uint32_t curCol = 0; curCol < aWidth; curCol++) {
   735       // Use sign bit to test for transparency, as alpha byte is highest byte
   736       if (*imageRow++ < 0)
   737         *outRow |= mask;
   739       mask >>= 1;
   740       if (!mask) {
   741         outRow ++;
   742         mask = 0x80;
   743       }
   744     }
   745   }
   747   return outData;
   748 }
   750 /**
   751  * Convert the given image data to a HBITMAP. If the requested depth is
   752  * 32 bit, a bitmap with an alpha channel will be returned.
   753  *
   754  * @param aImageData The image data to convert. Must use the format accepted
   755  *                   by CreateDIBitmap.
   756  * @param aWidth     With of the bitmap, in pixels.
   757  * @param aHeight    Height of the image, in pixels.
   758  * @param aDepth     Image depth, in bits. Should be one of 1, 24 and 32.
   759  *
   760  * @return The HBITMAP representing the image. Caller should call
   761  *         DeleteObject when done with the bitmap.
   762  *         On failure, nullptr will be returned.
   763  */
   764 HBITMAP nsWindowGfx::DataToBitmap(uint8_t* aImageData,
   765                                   uint32_t aWidth,
   766                                   uint32_t aHeight,
   767                                   uint32_t aDepth)
   768 {
   769   HDC dc = ::GetDC(nullptr);
   771   if (aDepth == 32) {
   772     // Alpha channel. We need the new header.
   773     BITMAPV4HEADER head = { 0 };
   774     head.bV4Size = sizeof(head);
   775     head.bV4Width = aWidth;
   776     head.bV4Height = aHeight;
   777     head.bV4Planes = 1;
   778     head.bV4BitCount = aDepth;
   779     head.bV4V4Compression = BI_BITFIELDS;
   780     head.bV4SizeImage = 0; // Uncompressed
   781     head.bV4XPelsPerMeter = 0;
   782     head.bV4YPelsPerMeter = 0;
   783     head.bV4ClrUsed = 0;
   784     head.bV4ClrImportant = 0;
   786     head.bV4RedMask   = 0x00FF0000;
   787     head.bV4GreenMask = 0x0000FF00;
   788     head.bV4BlueMask  = 0x000000FF;
   789     head.bV4AlphaMask = 0xFF000000;
   791     HBITMAP bmp = ::CreateDIBitmap(dc,
   792                                    reinterpret_cast<CONST BITMAPINFOHEADER*>(&head),
   793                                    CBM_INIT,
   794                                    aImageData,
   795                                    reinterpret_cast<CONST BITMAPINFO*>(&head),
   796                                    DIB_RGB_COLORS);
   797     ::ReleaseDC(nullptr, dc);
   798     return bmp;
   799   }
   801   char reserved_space[sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 2];
   802   BITMAPINFOHEADER& head = *(BITMAPINFOHEADER*)reserved_space;
   804   head.biSize = sizeof(BITMAPINFOHEADER);
   805   head.biWidth = aWidth;
   806   head.biHeight = aHeight;
   807   head.biPlanes = 1;
   808   head.biBitCount = (WORD)aDepth;
   809   head.biCompression = BI_RGB;
   810   head.biSizeImage = 0; // Uncompressed
   811   head.biXPelsPerMeter = 0;
   812   head.biYPelsPerMeter = 0;
   813   head.biClrUsed = 0;
   814   head.biClrImportant = 0;
   816   BITMAPINFO& bi = *(BITMAPINFO*)reserved_space;
   818   if (aDepth == 1) {
   819     RGBQUAD black = { 0, 0, 0, 0 };
   820     RGBQUAD white = { 255, 255, 255, 0 };
   822     bi.bmiColors[0] = white;
   823     bi.bmiColors[1] = black;
   824   }
   826   HBITMAP bmp = ::CreateDIBitmap(dc, &head, CBM_INIT, aImageData, &bi, DIB_RGB_COLORS);
   827   ::ReleaseDC(nullptr, dc);
   828   return bmp;
   829 }

mercurial