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.

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

mercurial