1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "WindowSurfaceX11Image.h"
9 #include "mozilla/gfx/2D.h"
10 #include "mozilla/gfx/Tools.h"
11 #include "mozilla/gfx/gfxVars.h"
12 #include "gfxPlatform.h"
13 #include "gfx2DGlue.h"
15 #include <X11/extensions/shape.h>
20 using namespace mozilla::gfx
;
22 // gfxImageSurface pixel format configuration.
23 #define SHAPED_IMAGE_SURFACE_BPP 4
25 # define SHAPED_IMAGE_SURFACE_ALPHA_INDEX 0
27 # define SHAPED_IMAGE_SURFACE_ALPHA_INDEX 3
30 WindowSurfaceX11Image::WindowSurfaceX11Image(Display
* aDisplay
, Window aWindow
,
34 : WindowSurfaceX11(aDisplay
, aWindow
, aVisual
, aDepth
),
35 mTransparencyBitmap(nullptr),
36 mTransparencyBitmapWidth(0),
37 mTransparencyBitmapHeight(0),
44 Window root
, *children
= nullptr;
45 unsigned int childrenNum
;
46 if (XQueryTree(mDisplay
, mWindow
, &root
, &mWindowParent
, &children
,
49 XFree((char*)children
);
54 WindowSurfaceX11Image::~WindowSurfaceX11Image() {
55 if (mTransparencyBitmap
) {
56 delete[] mTransparencyBitmap
;
60 already_AddRefed
<gfx::DrawTarget
> WindowSurfaceX11Image::Lock(
61 const LayoutDeviceIntRegion
& aRegion
) {
62 gfx::IntRect bounds
= aRegion
.GetBounds().ToUnknownRect();
63 gfx::IntSize
size(bounds
.XMost(), bounds
.YMost());
65 if (!mWindowSurface
|| mWindowSurface
->CairoStatus() ||
66 !(size
<= mWindowSurface
->GetSize())) {
67 mWindowSurface
= new gfxXlibSurface(mDisplay
, mWindow
, mVisual
, size
);
69 if (mWindowSurface
->CairoStatus()) {
73 if (!mImageSurface
|| mImageSurface
->CairoStatus() ||
74 !(size
<= mImageSurface
->GetSize())) {
75 gfxImageFormat format
= SurfaceFormatToImageFormat(mFormat
);
76 if (format
== gfx::SurfaceFormat::UNKNOWN
) {
77 format
= mDepth
== 32 ? gfx::SurfaceFormat::A8R8G8B8_UINT32
78 : gfx::SurfaceFormat::X8R8G8B8_UINT32
;
81 // Use alpha image format for shaped window as we derive
82 // the shape bitmap from alpha channel. Must match SHAPED_IMAGE_SURFACE_BPP
83 // and SHAPED_IMAGE_SURFACE_ALPHA_INDEX.
85 format
= gfx::SurfaceFormat::A8R8G8B8_UINT32
;
88 mImageSurface
= new gfxImageSurface(size
, format
);
89 if (mImageSurface
->CairoStatus()) {
94 gfxImageFormat format
= mImageSurface
->Format();
95 // Cairo prefers compositing to BGRX instead of BGRA where possible.
96 // Cairo/pixman lacks some fast paths for compositing BGRX onto BGRA, so
97 // just report it as BGRX directly in that case.
98 // Otherwise, for Skia, report it as BGRA to the compositor. The alpha
99 // channel will be discarded when we put the image.
100 if (format
== gfx::SurfaceFormat::X8R8G8B8_UINT32
) {
101 gfx::BackendType backend
= gfxVars::ContentBackend();
102 if (!gfx::Factory::DoesBackendSupportDataDrawtarget(backend
)) {
103 backend
= gfx::BackendType::SKIA
;
105 if (backend
!= gfx::BackendType::CAIRO
) {
106 format
= gfx::SurfaceFormat::A8R8G8B8_UINT32
;
110 return gfxPlatform::CreateDrawTargetForData(
111 mImageSurface
->Data(), mImageSurface
->GetSize(), mImageSurface
->Stride(),
112 ImageFormatToSurfaceFormat(format
));
115 // The transparency bitmap routines are derived form the ones at nsWindow.cpp.
116 // The difference here is that we compose to RGBA image and then create
117 // the shape mask from final image alpha channel.
118 static inline int32_t GetBitmapStride(int32_t width
) { return (width
+ 7) / 8; }
120 static bool ChangedMaskBits(gchar
* aMaskBits
, int32_t aMaskWidth
,
121 int32_t aMaskHeight
, const nsIntRect
& aRect
,
122 uint8_t* aImageData
) {
123 int32_t stride
= aMaskWidth
* SHAPED_IMAGE_SURFACE_BPP
;
124 int32_t x
, y
, xMax
= aRect
.XMost(), yMax
= aRect
.YMost();
125 int32_t maskBytesPerRow
= GetBitmapStride(aMaskWidth
);
126 for (y
= aRect
.y
; y
< yMax
; y
++) {
127 gchar
* maskBytes
= aMaskBits
+ y
* maskBytesPerRow
;
128 uint8_t* alphas
= aImageData
;
129 for (x
= aRect
.x
; x
< xMax
; x
++) {
130 bool newBit
= *(alphas
+ SHAPED_IMAGE_SURFACE_ALPHA_INDEX
) > 0x7f;
131 alphas
+= SHAPED_IMAGE_SURFACE_BPP
;
133 gchar maskByte
= maskBytes
[x
>> 3];
134 bool maskBit
= (maskByte
& (1 << (x
& 7))) != 0;
136 if (maskBit
!= newBit
) {
140 aImageData
+= stride
;
146 static void UpdateMaskBits(gchar
* aMaskBits
, int32_t aMaskWidth
,
147 int32_t aMaskHeight
, const nsIntRect
& aRect
,
148 uint8_t* aImageData
) {
149 int32_t stride
= aMaskWidth
* SHAPED_IMAGE_SURFACE_BPP
;
150 int32_t x
, y
, xMax
= aRect
.XMost(), yMax
= aRect
.YMost();
151 int32_t maskBytesPerRow
= GetBitmapStride(aMaskWidth
);
152 for (y
= aRect
.y
; y
< yMax
; y
++) {
153 gchar
* maskBytes
= aMaskBits
+ y
* maskBytesPerRow
;
154 uint8_t* alphas
= aImageData
;
155 for (x
= aRect
.x
; x
< xMax
; x
++) {
156 bool newBit
= *(alphas
+ SHAPED_IMAGE_SURFACE_ALPHA_INDEX
) > 0x7f;
157 alphas
+= SHAPED_IMAGE_SURFACE_BPP
;
159 gchar mask
= 1 << (x
& 7);
160 gchar maskByte
= maskBytes
[x
>> 3];
161 // Note: '-newBit' turns 0 into 00...00 and 1 into 11...11
162 maskBytes
[x
>> 3] = (maskByte
& ~mask
) | (-newBit
& mask
);
164 aImageData
+= stride
;
168 void WindowSurfaceX11Image::ResizeTransparencyBitmap(int aWidth
, int aHeight
) {
170 GetBitmapStride(mTransparencyBitmapWidth
) * mTransparencyBitmapHeight
;
171 int32_t newSize
= GetBitmapStride(aWidth
) * aHeight
;
173 if (actualSize
< newSize
) {
174 delete[] mTransparencyBitmap
;
175 mTransparencyBitmap
= new gchar
[newSize
];
178 mTransparencyBitmapWidth
= aWidth
;
179 mTransparencyBitmapHeight
= aHeight
;
182 void WindowSurfaceX11Image::ApplyTransparencyBitmap() {
183 gfx::IntSize size
= mWindowSurface
->GetSize();
184 bool maskChanged
= true;
186 if (!mTransparencyBitmap
) {
187 mTransparencyBitmapWidth
= size
.width
;
188 mTransparencyBitmapHeight
= size
.height
;
191 GetBitmapStride(mTransparencyBitmapWidth
) * mTransparencyBitmapHeight
;
192 mTransparencyBitmap
= new gchar
[byteSize
];
194 bool sizeChanged
= (size
.width
!= mTransparencyBitmapWidth
||
195 size
.height
!= mTransparencyBitmapHeight
);
198 ResizeTransparencyBitmap(size
.width
, size
.height
);
200 maskChanged
= ChangedMaskBits(
201 mTransparencyBitmap
, mTransparencyBitmapWidth
,
202 mTransparencyBitmapHeight
, nsIntRect(0, 0, size
.width
, size
.height
),
203 (uint8_t*)mImageSurface
->Data());
208 UpdateMaskBits(mTransparencyBitmap
, mTransparencyBitmapWidth
,
209 mTransparencyBitmapHeight
,
210 nsIntRect(0, 0, size
.width
, size
.height
),
211 (uint8_t*)mImageSurface
->Data());
213 // We use X11 calls where possible, because GDK handles expose events
214 // for shaped windows in a way that's incompatible with us (Bug 635903).
215 // It doesn't occur when the shapes are set through X.
216 Display
* xDisplay
= mWindowSurface
->XDisplay();
217 Window xDrawable
= mWindowSurface
->XDrawable();
218 Pixmap maskPixmap
= XCreateBitmapFromData(
219 xDisplay
, xDrawable
, mTransparencyBitmap
, mTransparencyBitmapWidth
,
220 mTransparencyBitmapHeight
);
221 XShapeCombineMask(xDisplay
, xDrawable
, ShapeBounding
, 0, 0, maskPixmap
,
224 XShapeCombineMask(mDisplay
, mWindowParent
, ShapeBounding
, 0, 0,
225 maskPixmap
, ShapeSet
);
227 XFreePixmap(xDisplay
, maskPixmap
);
231 void WindowSurfaceX11Image::Commit(
232 const LayoutDeviceIntRegion
& aInvalidRegion
) {
233 RefPtr
<gfx::DrawTarget
> dt
= gfx::Factory::CreateDrawTargetForCairoSurface(
234 mWindowSurface
->CairoSurface(), mWindowSurface
->GetSize());
235 RefPtr
<gfx::SourceSurface
> surf
=
236 gfx::Factory::CreateSourceSurfaceForCairoSurface(
237 mImageSurface
->CairoSurface(), mImageSurface
->GetSize(),
238 mImageSurface
->Format());
243 gfx::IntRect bounds
= aInvalidRegion
.GetBounds().ToUnknownRect();
244 if (bounds
.IsEmpty()) {
249 ApplyTransparencyBitmap();
252 uint32_t numRects
= aInvalidRegion
.GetNumRects();
254 dt
->CopySurface(surf
, bounds
, bounds
.TopLeft());
256 AutoTArray
<IntRect
, 32> rects
;
257 rects
.SetCapacity(numRects
);
258 for (auto iter
= aInvalidRegion
.RectIter(); !iter
.Done(); iter
.Next()) {
259 rects
.AppendElement(iter
.Get().ToUnknownRect());
261 dt
->PushDeviceSpaceClipRects(rects
.Elements(), rects
.Length());
263 dt
->DrawSurface(surf
, gfx::Rect(bounds
), gfx::Rect(bounds
),
264 DrawSurfaceOptions(),
265 DrawOptions(1.0f
, CompositionOp::OP_SOURCE
));
271 } // namespace widget
272 } // namespace mozilla