Source code

Revision control

Other Tools

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/. */
5
6
/*
7
* nsWindowGfx - Painting and aceleration.
8
*/
9
10
/**************************************************************
11
**************************************************************
12
**
13
** BLOCK: Includes
14
**
15
** Include headers.
16
**
17
**************************************************************
18
**************************************************************/
19
20
#include "mozilla/dom/ContentParent.h"
21
#include "mozilla/plugins/PluginInstanceParent.h"
22
using mozilla::plugins::PluginInstanceParent;
23
24
#include "nsWindowGfx.h"
25
#include "nsAppRunner.h"
26
#include <windows.h>
27
#include "gfxEnv.h"
28
#include "gfxImageSurface.h"
29
#include "gfxUtils.h"
30
#include "gfxWindowsSurface.h"
31
#include "gfxWindowsPlatform.h"
32
#include "gfxDWriteFonts.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 "mozilla/UniquePtrExtensions.h"
38
#include "nsGfxCIID.h"
39
#include "gfxContext.h"
40
#include "WinUtils.h"
41
#include "nsIWidgetListener.h"
42
#include "mozilla/Unused.h"
43
#include "nsDebug.h"
44
45
#include "mozilla/gfx/GPUProcessManager.h"
46
#include "mozilla/layers/CompositorBridgeParent.h"
47
#include "mozilla/layers/CompositorBridgeChild.h"
48
#include "ClientLayerManager.h"
49
#include "InProcessWinCompositorWidget.h"
50
51
#include "nsUXThemeData.h"
52
#include "nsUXThemeConstants.h"
53
54
using namespace mozilla;
55
using namespace mozilla::gfx;
56
using namespace mozilla::layers;
57
using namespace mozilla::widget;
58
using namespace mozilla::plugins;
59
60
/**************************************************************
61
**************************************************************
62
**
63
** BLOCK: Variables
64
**
65
** nsWindow Class static initializations and global variables.
66
**
67
**************************************************************
68
**************************************************************/
69
70
/**************************************************************
71
*
72
* SECTION: nsWindow statics
73
*
74
**************************************************************/
75
76
static UniquePtr<uint8_t[]> sSharedSurfaceData;
77
static IntSize sSharedSurfaceSize;
78
79
struct IconMetrics {
80
int32_t xMetric;
81
int32_t yMetric;
82
int32_t defaultSize;
83
};
84
85
// Corresponds 1:1 to the IconSizeType enum
86
static IconMetrics sIconMetrics[] = {
87
{SM_CXSMICON, SM_CYSMICON, 16}, // small icon
88
{SM_CXICON, SM_CYICON, 32} // regular icon
89
};
90
91
/**************************************************************
92
**************************************************************
93
**
94
** BLOCK: nsWindow impl.
95
**
96
** Paint related nsWindow methods.
97
**
98
**************************************************************
99
**************************************************************/
100
101
// GetRegionToPaint returns the invalidated region that needs to be painted
102
LayoutDeviceIntRegion nsWindow::GetRegionToPaint(bool aForceFullRepaint,
103
PAINTSTRUCT ps, HDC aDC) {
104
if (aForceFullRepaint) {
105
RECT paintRect;
106
::GetClientRect(mWnd, &paintRect);
107
return LayoutDeviceIntRegion(WinUtils::ToIntRect(paintRect));
108
}
109
110
HRGN paintRgn = ::CreateRectRgn(0, 0, 0, 0);
111
if (paintRgn != nullptr) {
112
int result = GetRandomRgn(aDC, paintRgn, SYSRGN);
113
if (result == 1) {
114
POINT pt = {0, 0};
115
::MapWindowPoints(nullptr, mWnd, &pt, 1);
116
::OffsetRgn(paintRgn, pt.x, pt.y);
117
}
118
LayoutDeviceIntRegion rgn(WinUtils::ConvertHRGNToRegion(paintRgn));
119
::DeleteObject(paintRgn);
120
return rgn;
121
}
122
return LayoutDeviceIntRegion(WinUtils::ToIntRect(ps.rcPaint));
123
}
124
125
nsIWidgetListener* nsWindow::GetPaintListener() {
126
if (mDestroyCalled) return nullptr;
127
return mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener;
128
}
129
130
void nsWindow::ForcePresent() {
131
if (mResizeState != RESIZING) {
132
if (CompositorBridgeChild* remoteRenderer = GetRemoteRenderer()) {
133
remoteRenderer->SendForcePresent();
134
}
135
}
136
}
137
138
bool nsWindow::OnPaint(HDC aDC, uint32_t aNestingLevel) {
139
// We never have reentrant paint events, except when we're running our RPC
140
// windows event spin loop. If we don't trap for this, we'll try to paint,
141
// but view manager will refuse to paint the surface, resulting is black
142
// flashes on the plugin rendering surface.
143
if (mozilla::ipc::MessageChannel::IsSpinLoopActive() && mPainting)
144
return false;
145
146
DeviceResetReason resetReason = DeviceResetReason::OK;
147
if (gfxWindowsPlatform::GetPlatform()->DidRenderingDeviceReset(
148
&resetReason)) {
149
gfxCriticalNote << "(nsWindow) Detected device reset: " << (int)resetReason;
150
151
gfxWindowsPlatform::GetPlatform()->UpdateRenderMode();
152
153
GPUProcessManager::Get()->OnInProcessDeviceReset();
154
155
gfxCriticalNote << "(nsWindow) Finished device reset.";
156
return false;
157
}
158
159
// After we CallUpdateWindow to the child, occasionally a WM_PAINT message
160
// is posted to the parent event loop with an empty update rect. Do a
161
// dummy paint so that Windows stops dispatching WM_PAINT in an inifinite
162
// loop. See bug 543788.
163
if (IsPlugin()) {
164
RECT updateRect;
165
if (!GetUpdateRect(mWnd, &updateRect, FALSE) ||
166
(updateRect.left == updateRect.right &&
167
updateRect.top == updateRect.bottom)) {
168
PAINTSTRUCT ps;
169
BeginPaint(mWnd, &ps);
170
EndPaint(mWnd, &ps);
171
return true;
172
}
173
174
if (mWindowType == eWindowType_plugin_ipc_chrome) {
175
// Fire off an async request to the plugin to paint its window
176
mozilla::dom::ContentParent::SendAsyncUpdate(this);
177
ValidateRect(mWnd, nullptr);
178
return true;
179
}
180
181
PluginInstanceParent* instance = reinterpret_cast<PluginInstanceParent*>(
182
::GetPropW(mWnd, L"PluginInstanceParentProperty"));
183
if (instance) {
184
Unused << instance->CallUpdateWindow();
185
} else {
186
// We should never get here since in-process plugins should have
187
// subclassed our HWND and handled WM_PAINT, but in some cases that
188
// could fail. Return without asserting since it's not our fault.
189
NS_WARNING("Plugin failed to subclass our window");
190
}
191
192
ValidateRect(mWnd, nullptr);
193
return true;
194
}
195
196
PAINTSTRUCT ps;
197
198
// Avoid starting the GPU process for the initial navigator:blank window.
199
if (mIsEarlyBlankWindow) {
200
// Call BeginPaint/EndPaint or Windows will keep sending us messages.
201
::BeginPaint(mWnd, &ps);
202
::EndPaint(mWnd, &ps);
203
return true;
204
}
205
206
// Clear window by transparent black when compositor window is used in GPU
207
// process and non-client area rendering by DWM is enabled.
208
// It is for showing non-client area rendering. See nsWindow::UpdateGlass().
209
if (HasGlass() && GetLayerManager()->AsKnowsCompositor() &&
210
GetLayerManager()->AsKnowsCompositor()->GetUseCompositorWnd()) {
211
HDC hdc;
212
RECT rect;
213
hdc = ::GetWindowDC(mWnd);
214
::GetWindowRect(mWnd, &rect);
215
::MapWindowPoints(nullptr, mWnd, (LPPOINT)&rect, 2);
216
::FillRect(hdc, &rect,
217
reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)));
218
ReleaseDC(mWnd, hdc);
219
}
220
221
if (GetLayerManager()->AsKnowsCompositor() &&
222
!mBounds.IsEqualEdges(mLastPaintBounds)) {
223
// Do an early async composite so that we at least have something on the
224
// screen in the right place, even if the content is out of date.
225
GetLayerManager()->ScheduleComposite();
226
}
227
mLastPaintBounds = mBounds;
228
229
#ifdef MOZ_XUL
230
if (!aDC &&
231
(GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC) &&
232
(eTransparencyTransparent == mTransparencyMode)) {
233
// For layered translucent windows all drawing should go to memory DC and no
234
// WM_PAINT messages are normally generated. To support asynchronous
235
// painting we force generation of WM_PAINT messages by invalidating window
236
// areas with RedrawWindow, InvalidateRect or InvalidateRgn function calls.
237
// BeginPaint/EndPaint must be called to make Windows think that invalid
238
// area is painted. Otherwise it will continue sending the same message
239
// endlessly.
240
::BeginPaint(mWnd, &ps);
241
::EndPaint(mWnd, &ps);
242
243
// We're guaranteed to have a widget proxy since we called
244
// GetLayerManager().
245
aDC = mBasicLayersSurface->GetTransparentDC();
246
}
247
#endif
248
249
mPainting = true;
250
251
#ifdef WIDGET_DEBUG_OUTPUT
252
HRGN debugPaintFlashRegion = nullptr;
253
HDC debugPaintFlashDC = nullptr;
254
255
if (debug_WantPaintFlashing()) {
256
debugPaintFlashRegion = ::CreateRectRgn(0, 0, 0, 0);
257
::GetUpdateRgn(mWnd, debugPaintFlashRegion, TRUE);
258
debugPaintFlashDC = ::GetDC(mWnd);
259
}
260
#endif // WIDGET_DEBUG_OUTPUT
261
262
HDC hDC = aDC ? aDC : (::BeginPaint(mWnd, &ps));
263
mPaintDC = hDC;
264
265
#ifdef MOZ_XUL
266
bool forceRepaint = aDC || (eTransparencyTransparent == mTransparencyMode);
267
#else
268
bool forceRepaint = nullptr != aDC;
269
#endif
270
LayoutDeviceIntRegion region = GetRegionToPaint(forceRepaint, ps, hDC);
271
272
if (GetLayerManager()->AsKnowsCompositor()) {
273
// We need to paint to the screen even if nothing changed, since if we
274
// don't have a compositing window manager, our pixels could be stale.
275
GetLayerManager()->SetNeedsComposite(true);
276
GetLayerManager()->SendInvalidRegion(region.ToUnknownRegion());
277
}
278
279
RefPtr<nsWindow> strongThis(this);
280
281
nsIWidgetListener* listener = GetPaintListener();
282
if (listener) {
283
listener->WillPaintWindow(this);
284
}
285
// Re-get the listener since the will paint notification may have killed it.
286
listener = GetPaintListener();
287
if (!listener) {
288
return false;
289
}
290
291
if (GetLayerManager()->AsKnowsCompositor() &&
292
GetLayerManager()->NeedsComposite()) {
293
GetLayerManager()->ScheduleComposite();
294
GetLayerManager()->SetNeedsComposite(false);
295
}
296
297
bool result = true;
298
if (!region.IsEmpty() && listener) {
299
// Should probably pass in a real region here, using GetRandomRgn
301
302
#ifdef WIDGET_DEBUG_OUTPUT
303
debug_DumpPaintEvent(stdout, this, region.ToUnknownRegion(), "noname",
304
(int32_t)mWnd);
305
#endif // WIDGET_DEBUG_OUTPUT
306
307
switch (GetLayerManager()->GetBackendType()) {
308
case LayersBackend::LAYERS_BASIC: {
309
RefPtr<gfxASurface> targetSurface;
310
311
#if defined(MOZ_XUL)
312
// don't support transparency for non-GDI rendering, for now
313
if (eTransparencyTransparent == mTransparencyMode) {
314
// This mutex needs to be held when EnsureTransparentSurface is
315
// called.
316
MutexAutoLock lock(mBasicLayersSurface->GetTransparentSurfaceLock());
317
targetSurface = mBasicLayersSurface->EnsureTransparentSurface();
318
}
319
#endif
320
321
RefPtr<gfxWindowsSurface> targetSurfaceWin;
322
if (!targetSurface) {
323
uint32_t flags = (mTransparencyMode == eTransparencyOpaque)
324
? 0
325
: gfxWindowsSurface::FLAG_IS_TRANSPARENT;
326
targetSurfaceWin = new gfxWindowsSurface(hDC, flags);
327
targetSurface = targetSurfaceWin;
328
}
329
330
if (!targetSurface) {
331
NS_ERROR("Invalid RenderMode!");
332
return false;
333
}
334
335
RECT paintRect;
336
::GetClientRect(mWnd, &paintRect);
337
RefPtr<DrawTarget> dt = gfxPlatform::CreateDrawTargetForSurface(
338
targetSurface, IntSize(paintRect.right - paintRect.left,
339
paintRect.bottom - paintRect.top));
340
if (!dt || !dt->IsValid()) {
341
gfxWarning()
342
<< "nsWindow::OnPaint failed in CreateDrawTargetForSurface";
343
return false;
344
}
345
346
// don't need to double buffer with anything but GDI
347
BufferMode doubleBuffering = mozilla::layers::BufferMode::BUFFER_NONE;
348
#ifdef MOZ_XUL
349
switch (mTransparencyMode) {
350
case eTransparencyGlass:
351
case eTransparencyBorderlessGlass:
352
default:
353
// If we're not doing translucency, then double buffer
354
doubleBuffering = mozilla::layers::BufferMode::BUFFERED;
355
break;
356
case eTransparencyTransparent:
357
// If we're rendering with translucency, we're going to be
358
// rendering the whole window; make sure we clear it first
359
dt->ClearRect(
360
Rect(0.f, 0.f, dt->GetSize().width, dt->GetSize().height));
361
break;
362
}
363
#else
364
doubleBuffering = mozilla::layers::BufferMode::BUFFERED;
365
#endif
366
367
RefPtr<gfxContext> thebesContext = gfxContext::CreateOrNull(dt);
368
MOZ_ASSERT(thebesContext); // already checked draw target above
369
370
{
371
AutoLayerManagerSetup setupLayerManager(this, thebesContext,
372
doubleBuffering);
373
result = listener->PaintWindow(this, region);
374
}
375
376
#ifdef MOZ_XUL
377
if (eTransparencyTransparent == mTransparencyMode) {
378
// Data from offscreen drawing surface was copied to memory bitmap of
379
// transparent bitmap. Now it can be read from memory bitmap to apply
380
// alpha channel and after that displayed on the screen.
381
mBasicLayersSurface->RedrawTransparentWindow();
382
}
383
#endif
384
} break;
385
case LayersBackend::LAYERS_CLIENT:
386
case LayersBackend::LAYERS_WR: {
387
result = listener->PaintWindow(this, region);
388
if (!gfxEnv::DisableForcePresent() &&
389
gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
390
nsCOMPtr<nsIRunnable> event = NewRunnableMethod(
391
"nsWindow::ForcePresent", this, &nsWindow::ForcePresent);
392
NS_DispatchToMainThread(event);
393
}
394
} break;
395
default:
396
NS_ERROR("Unknown layers backend used!");
397
break;
398
}
399
}
400
401
if (!aDC) {
402
::EndPaint(mWnd, &ps);
403
}
404
405
mPaintDC = nullptr;
406
mLastPaintEndTime = TimeStamp::Now();
407
408
#if defined(WIDGET_DEBUG_OUTPUT)
409
if (debug_WantPaintFlashing()) {
410
// Only flash paint events which have not ignored the paint message.
411
// Those that ignore the paint message aren't painting anything so there
412
// is only the overhead of the dispatching the paint event.
413
if (result) {
414
::InvertRgn(debugPaintFlashDC, debugPaintFlashRegion);
415
PR_Sleep(PR_MillisecondsToInterval(30));
416
::InvertRgn(debugPaintFlashDC, debugPaintFlashRegion);
417
PR_Sleep(PR_MillisecondsToInterval(30));
418
}
419
::ReleaseDC(mWnd, debugPaintFlashDC);
420
::DeleteObject(debugPaintFlashRegion);
421
}
422
#endif // WIDGET_DEBUG_OUTPUT
423
424
mPainting = false;
425
426
// Re-get the listener since painting may have killed it.
427
listener = GetPaintListener();
428
if (listener) listener->DidPaintWindow();
429
430
if (aNestingLevel == 0 && ::GetUpdateRect(mWnd, nullptr, false)) {
431
OnPaint(aDC, 1);
432
}
433
434
return result;
435
}
436
437
// This override of CreateCompositor is to add support for sending the IPC
438
// call for RequesetFxrOutput as soon as the compositor for this widget is
439
// available.
440
void nsWindow::CreateCompositor() {
441
nsWindowBase::CreateCompositor();
442
443
if (mRequestFxrOutputPending) {
444
GetRemoteRenderer()->SendRequestFxrOutput();
445
}
446
}
447
448
void nsWindow::RequestFxrOutput() {
449
if (GetRemoteRenderer() != nullptr) {
450
MOZ_CRASH("RequestFxrOutput should happen before Compositor is created.");
451
} else {
452
// The compositor isn't ready, so indicate to make the IPC call when
453
// it is available.
454
mRequestFxrOutputPending = true;
455
}
456
}
457
458
LayoutDeviceIntSize nsWindowGfx::GetIconMetrics(IconSizeType aSizeType) {
459
int32_t width = ::GetSystemMetrics(sIconMetrics[aSizeType].xMetric);
460
int32_t height = ::GetSystemMetrics(sIconMetrics[aSizeType].yMetric);
461
462
if (width == 0 || height == 0) {
463
width = height = sIconMetrics[aSizeType].defaultSize;
464
}
465
466
return LayoutDeviceIntSize(width, height);
467
}
468
469
nsresult nsWindowGfx::CreateIcon(imgIContainer* aContainer, bool aIsCursor,
470
LayoutDeviceIntPoint aHotspot,
471
LayoutDeviceIntSize aScaledSize,
472
HICON* aIcon) {
473
MOZ_ASSERT(aHotspot.x >= 0 && aHotspot.y >= 0);
474
MOZ_ASSERT((aScaledSize.width > 0 && aScaledSize.height > 0) ||
475
(aScaledSize.width == 0 && aScaledSize.height == 0));
476
477
// Get the image data
478
RefPtr<SourceSurface> surface = aContainer->GetFrame(
479
imgIContainer::FRAME_CURRENT,
480
imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
481
NS_ENSURE_TRUE(surface, NS_ERROR_NOT_AVAILABLE);
482
483
IntSize frameSize = surface->GetSize();
484
if (frameSize.IsEmpty()) {
485
return NS_ERROR_FAILURE;
486
}
487
488
IntSize iconSize(aScaledSize.width, aScaledSize.height);
489
if (iconSize == IntSize(0, 0)) { // use frame's intrinsic size
490
iconSize = frameSize;
491
}
492
493
RefPtr<DataSourceSurface> dataSurface;
494
bool mappedOK;
495
DataSourceSurface::MappedSurface map;
496
497
if (iconSize != frameSize) {
498
// Scale the surface
499
dataSurface =
500
Factory::CreateDataSourceSurface(iconSize, SurfaceFormat::B8G8R8A8);
501
NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
502
mappedOK = dataSurface->Map(DataSourceSurface::MapType::READ_WRITE, &map);
503
NS_ENSURE_TRUE(mappedOK, NS_ERROR_FAILURE);
504
505
RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
506
BackendType::CAIRO, map.mData, dataSurface->GetSize(), map.mStride,
507
SurfaceFormat::B8G8R8A8);
508
if (!dt) {
509
gfxWarning()
510
<< "nsWindowGfx::CreatesIcon failed in CreateDrawTargetForData";
511
return NS_ERROR_OUT_OF_MEMORY;
512
}
513
dt->DrawSurface(surface, Rect(0, 0, iconSize.width, iconSize.height),
514
Rect(0, 0, frameSize.width, frameSize.height),
515
DrawSurfaceOptions(),
516
DrawOptions(1.0f, CompositionOp::OP_SOURCE));
517
} else if (surface->GetFormat() != SurfaceFormat::B8G8R8A8) {
518
// Convert format to SurfaceFormat::B8G8R8A8
519
dataSurface = gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(
520
surface, SurfaceFormat::B8G8R8A8);
521
NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
522
mappedOK = dataSurface->Map(DataSourceSurface::MapType::READ, &map);
523
} else {
524
dataSurface = surface->GetDataSurface();
525
NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
526
mappedOK = dataSurface->Map(DataSourceSurface::MapType::READ, &map);
527
}
528
NS_ENSURE_TRUE(dataSurface && mappedOK, NS_ERROR_FAILURE);
529
MOZ_ASSERT(dataSurface->GetFormat() == SurfaceFormat::B8G8R8A8);
530
531
uint8_t* data = nullptr;
532
UniquePtr<uint8_t[]> autoDeleteArray;
533
if (map.mStride == BytesPerPixel(dataSurface->GetFormat()) * iconSize.width) {
534
// Mapped data is already packed
535
data = map.mData;
536
} else {
537
// We can't use map.mData since the pixels are not packed (as required by
538
// CreateDIBitmap, which is called under the DataToBitmap call below).
539
//
540
// We must unmap before calling SurfaceToPackedBGRA because it needs access
541
// to the pixel data.
542
dataSurface->Unmap();
543
map.mData = nullptr;
544
545
autoDeleteArray = SurfaceToPackedBGRA(dataSurface);
546
data = autoDeleteArray.get();
547
NS_ENSURE_TRUE(data, NS_ERROR_FAILURE);
548
}
549
550
HBITMAP bmp = DataToBitmap(data, iconSize.width, -iconSize.height, 32);
551
uint8_t* a1data = Data32BitTo1Bit(data, iconSize.width, iconSize.height);
552
if (map.mData) {
553
dataSurface->Unmap();
554
}
555
if (!a1data) {
556
return NS_ERROR_FAILURE;
557
}
558
559
HBITMAP mbmp = DataToBitmap(a1data, iconSize.width, -iconSize.height, 1);
560
free(a1data);
561
562
ICONINFO info = {0};
563
info.fIcon = !aIsCursor;
564
info.xHotspot = aHotspot.x;
565
info.yHotspot = aHotspot.y;
566
info.hbmMask = mbmp;
567
info.hbmColor = bmp;
568
569
HCURSOR icon = ::CreateIconIndirect(&info);
570
::DeleteObject(mbmp);
571
::DeleteObject(bmp);
572
if (!icon) return NS_ERROR_FAILURE;
573
*aIcon = icon;
574
return NS_OK;
575
}
576
577
// Adjust cursor image data
578
uint8_t* nsWindowGfx::Data32BitTo1Bit(uint8_t* aImageData, uint32_t aWidth,
579
uint32_t aHeight) {
580
// We need (aWidth + 7) / 8 bytes plus zero-padding up to a multiple of
581
// 4 bytes for each row (HBITMAP requirement). Bug 353553.
582
uint32_t outBpr = ((aWidth + 31) / 8) & ~3;
583
584
// Allocate and clear mask buffer
585
uint8_t* outData = (uint8_t*)calloc(outBpr, aHeight);
586
if (!outData) return nullptr;
587
588
int32_t* imageRow = (int32_t*)aImageData;
589
for (uint32_t curRow = 0; curRow < aHeight; curRow++) {
590
uint8_t* outRow = outData + curRow * outBpr;
591
uint8_t mask = 0x80;
592
for (uint32_t curCol = 0; curCol < aWidth; curCol++) {
593
// Use sign bit to test for transparency, as alpha byte is highest byte
594
if (*imageRow++ < 0) *outRow |= mask;
595
596
mask >>= 1;
597
if (!mask) {
598
outRow++;
599
mask = 0x80;
600
}
601
}
602
}
603
604
return outData;
605
}
606
607
/**
608
* Convert the given image data to a HBITMAP. If the requested depth is
609
* 32 bit, a bitmap with an alpha channel will be returned.
610
*
611
* @param aImageData The image data to convert. Must use the format accepted
612
* by CreateDIBitmap.
613
* @param aWidth With of the bitmap, in pixels.
614
* @param aHeight Height of the image, in pixels.
615
* @param aDepth Image depth, in bits. Should be one of 1, 24 and 32.
616
*
617
* @return The HBITMAP representing the image. Caller should call
618
* DeleteObject when done with the bitmap.
619
* On failure, nullptr will be returned.
620
*/
621
HBITMAP nsWindowGfx::DataToBitmap(uint8_t* aImageData, uint32_t aWidth,
622
uint32_t aHeight, uint32_t aDepth) {
623
HDC dc = ::GetDC(nullptr);
624
625
if (aDepth == 32) {
626
// Alpha channel. We need the new header.
627
BITMAPV4HEADER head = {0};
628
head.bV4Size = sizeof(head);
629
head.bV4Width = aWidth;
630
head.bV4Height = aHeight;
631
head.bV4Planes = 1;
632
head.bV4BitCount = aDepth;
633
head.bV4V4Compression = BI_BITFIELDS;
634
head.bV4SizeImage = 0; // Uncompressed
635
head.bV4XPelsPerMeter = 0;
636
head.bV4YPelsPerMeter = 0;
637
head.bV4ClrUsed = 0;
638
head.bV4ClrImportant = 0;
639
640
head.bV4RedMask = 0x00FF0000;
641
head.bV4GreenMask = 0x0000FF00;
642
head.bV4BlueMask = 0x000000FF;
643
head.bV4AlphaMask = 0xFF000000;
644
645
HBITMAP bmp = ::CreateDIBitmap(
646
dc, reinterpret_cast<CONST BITMAPINFOHEADER*>(&head), CBM_INIT,
647
aImageData, reinterpret_cast<CONST BITMAPINFO*>(&head), DIB_RGB_COLORS);
648
::ReleaseDC(nullptr, dc);
649
return bmp;
650
}
651
652
char reserved_space[sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 2];
653
BITMAPINFOHEADER& head = *(BITMAPINFOHEADER*)reserved_space;
654
655
head.biSize = sizeof(BITMAPINFOHEADER);
656
head.biWidth = aWidth;
657
head.biHeight = aHeight;
658
head.biPlanes = 1;
659
head.biBitCount = (WORD)aDepth;
660
head.biCompression = BI_RGB;
661
head.biSizeImage = 0; // Uncompressed
662
head.biXPelsPerMeter = 0;
663
head.biYPelsPerMeter = 0;
664
head.biClrUsed = 0;
665
head.biClrImportant = 0;
666
667
BITMAPINFO& bi = *(BITMAPINFO*)reserved_space;
668
669
if (aDepth == 1) {
670
RGBQUAD black = {0, 0, 0, 0};
671
RGBQUAD white = {255, 255, 255, 0};
672
673
bi.bmiColors[0] = white;
674
bi.bmiColors[1] = black;
675
}
676
677
HBITMAP bmp =
678
::CreateDIBitmap(dc, &head, CBM_INIT, aImageData, &bi, DIB_RGB_COLORS);
679
::ReleaseDC(nullptr, dc);
680
return bmp;
681
}