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),
38 mIsShaped(aIsShaped
) {}
40 WindowSurfaceX11Image::~WindowSurfaceX11Image() {
41 if (mTransparencyBitmap
) {
42 delete[] mTransparencyBitmap
;
46 already_AddRefed
<gfx::DrawTarget
> WindowSurfaceX11Image::Lock(
47 const LayoutDeviceIntRegion
& aRegion
) {
48 gfx::IntRect bounds
= aRegion
.GetBounds().ToUnknownRect();
49 gfx::IntSize
size(bounds
.XMost(), bounds
.YMost());
51 if (!mWindowSurface
|| mWindowSurface
->CairoStatus() ||
52 !(size
<= mWindowSurface
->GetSize())) {
53 mWindowSurface
= new gfxXlibSurface(mDisplay
, mWindow
, mVisual
, size
);
55 if (mWindowSurface
->CairoStatus()) {
59 if (!mImageSurface
|| mImageSurface
->CairoStatus() ||
60 !(size
<= mImageSurface
->GetSize())) {
61 gfxImageFormat format
= SurfaceFormatToImageFormat(mFormat
);
62 if (format
== gfx::SurfaceFormat::UNKNOWN
) {
63 format
= mDepth
== 32 ? gfx::SurfaceFormat::A8R8G8B8_UINT32
64 : gfx::SurfaceFormat::X8R8G8B8_UINT32
;
67 // Use alpha image format for shaped window as we derive
68 // the shape bitmap from alpha channel. Must match SHAPED_IMAGE_SURFACE_BPP
69 // and SHAPED_IMAGE_SURFACE_ALPHA_INDEX.
71 format
= gfx::SurfaceFormat::A8R8G8B8_UINT32
;
74 mImageSurface
= new gfxImageSurface(size
, format
);
75 if (mImageSurface
->CairoStatus()) {
80 gfxImageFormat format
= mImageSurface
->Format();
81 // Cairo prefers compositing to BGRX instead of BGRA where possible.
82 // Cairo/pixman lacks some fast paths for compositing BGRX onto BGRA, so
83 // just report it as BGRX directly in that case.
84 // Otherwise, for Skia, report it as BGRA to the compositor. The alpha
85 // channel will be discarded when we put the image.
86 if (format
== gfx::SurfaceFormat::X8R8G8B8_UINT32
) {
87 gfx::BackendType backend
= gfxVars::ContentBackend();
88 if (!gfx::Factory::DoesBackendSupportDataDrawtarget(backend
)) {
89 backend
= gfx::BackendType::SKIA
;
91 if (backend
!= gfx::BackendType::CAIRO
) {
92 format
= gfx::SurfaceFormat::A8R8G8B8_UINT32
;
96 return gfxPlatform::CreateDrawTargetForData(
97 mImageSurface
->Data(), mImageSurface
->GetSize(), mImageSurface
->Stride(),
98 ImageFormatToSurfaceFormat(format
));
101 // The transparency bitmap routines are derived form the ones at nsWindow.cpp.
102 // The difference here is that we compose to RGBA image and then create
103 // the shape mask from final image alpha channel.
104 static inline int32_t GetBitmapStride(int32_t width
) { return (width
+ 7) / 8; }
106 static bool ChangedMaskBits(gchar
* aMaskBits
, int32_t aMaskWidth
,
107 int32_t aMaskHeight
, const nsIntRect
& aRect
,
108 uint8_t* aImageData
) {
109 int32_t stride
= aMaskWidth
* SHAPED_IMAGE_SURFACE_BPP
;
110 int32_t x
, y
, xMax
= aRect
.XMost(), yMax
= aRect
.YMost();
111 int32_t maskBytesPerRow
= GetBitmapStride(aMaskWidth
);
112 for (y
= aRect
.y
; y
< yMax
; y
++) {
113 gchar
* maskBytes
= aMaskBits
+ y
* maskBytesPerRow
;
114 uint8_t* alphas
= aImageData
;
115 for (x
= aRect
.x
; x
< xMax
; x
++) {
116 bool newBit
= *(alphas
+ SHAPED_IMAGE_SURFACE_ALPHA_INDEX
) > 0x7f;
117 alphas
+= SHAPED_IMAGE_SURFACE_BPP
;
119 gchar maskByte
= maskBytes
[x
>> 3];
120 bool maskBit
= (maskByte
& (1 << (x
& 7))) != 0;
122 if (maskBit
!= newBit
) {
126 aImageData
+= stride
;
132 static void UpdateMaskBits(gchar
* aMaskBits
, int32_t aMaskWidth
,
133 int32_t aMaskHeight
, const nsIntRect
& aRect
,
134 uint8_t* aImageData
) {
135 int32_t stride
= aMaskWidth
* SHAPED_IMAGE_SURFACE_BPP
;
136 int32_t x
, y
, xMax
= aRect
.XMost(), yMax
= aRect
.YMost();
137 int32_t maskBytesPerRow
= GetBitmapStride(aMaskWidth
);
138 for (y
= aRect
.y
; y
< yMax
; y
++) {
139 gchar
* maskBytes
= aMaskBits
+ y
* maskBytesPerRow
;
140 uint8_t* alphas
= aImageData
;
141 for (x
= aRect
.x
; x
< xMax
; x
++) {
142 bool newBit
= *(alphas
+ SHAPED_IMAGE_SURFACE_ALPHA_INDEX
) > 0x7f;
143 alphas
+= SHAPED_IMAGE_SURFACE_BPP
;
145 gchar mask
= 1 << (x
& 7);
146 gchar maskByte
= maskBytes
[x
>> 3];
147 // Note: '-newBit' turns 0 into 00...00 and 1 into 11...11
148 maskBytes
[x
>> 3] = (maskByte
& ~mask
) | (-newBit
& mask
);
150 aImageData
+= stride
;
154 void WindowSurfaceX11Image::ResizeTransparencyBitmap(int aWidth
, int aHeight
) {
156 GetBitmapStride(mTransparencyBitmapWidth
) * mTransparencyBitmapHeight
;
157 int32_t newSize
= GetBitmapStride(aWidth
) * aHeight
;
159 if (actualSize
< newSize
) {
160 delete[] mTransparencyBitmap
;
161 mTransparencyBitmap
= new gchar
[newSize
];
164 mTransparencyBitmapWidth
= aWidth
;
165 mTransparencyBitmapHeight
= aHeight
;
168 void WindowSurfaceX11Image::ApplyTransparencyBitmap() {
169 gfx::IntSize size
= mWindowSurface
->GetSize();
170 bool maskChanged
= true;
172 if (!mTransparencyBitmap
) {
173 mTransparencyBitmapWidth
= size
.width
;
174 mTransparencyBitmapHeight
= size
.height
;
177 GetBitmapStride(mTransparencyBitmapWidth
) * mTransparencyBitmapHeight
;
178 mTransparencyBitmap
= new gchar
[byteSize
];
180 bool sizeChanged
= (size
.width
!= mTransparencyBitmapWidth
||
181 size
.height
!= mTransparencyBitmapHeight
);
184 ResizeTransparencyBitmap(size
.width
, size
.height
);
186 maskChanged
= ChangedMaskBits(
187 mTransparencyBitmap
, mTransparencyBitmapWidth
,
188 mTransparencyBitmapHeight
, nsIntRect(0, 0, size
.width
, size
.height
),
189 (uint8_t*)mImageSurface
->Data());
194 UpdateMaskBits(mTransparencyBitmap
, mTransparencyBitmapWidth
,
195 mTransparencyBitmapHeight
,
196 nsIntRect(0, 0, size
.width
, size
.height
),
197 (uint8_t*)mImageSurface
->Data());
199 // We use X11 calls where possible, because GDK handles expose events
200 // for shaped windows in a way that's incompatible with us (Bug 635903).
201 // It doesn't occur when the shapes are set through X.
202 Display
* xDisplay
= mWindowSurface
->XDisplay();
203 Window xDrawable
= mWindowSurface
->XDrawable();
204 Pixmap maskPixmap
= XCreateBitmapFromData(
205 xDisplay
, xDrawable
, mTransparencyBitmap
, mTransparencyBitmapWidth
,
206 mTransparencyBitmapHeight
);
207 XShapeCombineMask(xDisplay
, xDrawable
, ShapeBounding
, 0, 0, maskPixmap
,
209 XFreePixmap(xDisplay
, maskPixmap
);
213 void WindowSurfaceX11Image::Commit(
214 const LayoutDeviceIntRegion
& aInvalidRegion
) {
215 RefPtr
<gfx::DrawTarget
> dt
= gfx::Factory::CreateDrawTargetForCairoSurface(
216 mWindowSurface
->CairoSurface(), mWindowSurface
->GetSize());
217 RefPtr
<gfx::SourceSurface
> surf
=
218 gfx::Factory::CreateSourceSurfaceForCairoSurface(
219 mImageSurface
->CairoSurface(), mImageSurface
->GetSize(),
220 mImageSurface
->Format());
225 gfx::IntRect bounds
= aInvalidRegion
.GetBounds().ToUnknownRect();
226 if (bounds
.IsEmpty()) {
231 ApplyTransparencyBitmap();
234 uint32_t numRects
= aInvalidRegion
.GetNumRects();
236 dt
->CopySurface(surf
, bounds
, bounds
.TopLeft());
238 AutoTArray
<IntRect
, 32> rects
;
239 rects
.SetCapacity(numRects
);
240 for (auto iter
= aInvalidRegion
.RectIter(); !iter
.Done(); iter
.Next()) {
241 rects
.AppendElement(iter
.Get().ToUnknownRect());
243 dt
->PushDeviceSpaceClipRects(rects
.Elements(), rects
.Length());
245 dt
->DrawSurface(surf
, gfx::Rect(bounds
), gfx::Rect(bounds
),
246 DrawSurfaceOptions(),
247 DrawOptions(1.0f
, CompositionOp::OP_SOURCE
));
253 } // namespace widget
254 } // namespace mozilla