1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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/. */
6
7 #include "WindowSurfaceX11Image.h"
8
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"
14
15 #include <X11/extensions/shape.h>
16
17 namespace mozilla {
18 namespace widget {
19
20 using namespace mozilla::gfx;
21
22 // gfxImageSurface pixel format configuration.
23 #define SHAPED_IMAGE_SURFACE_BPP 4
24 #ifdef IS_BIG_ENDIAN
25 # define SHAPED_IMAGE_SURFACE_ALPHA_INDEX 0
26 #else
27 # define SHAPED_IMAGE_SURFACE_ALPHA_INDEX 3
28 #endif
29
WindowSurfaceX11Image(Display * aDisplay,Window aWindow,Visual * aVisual,unsigned int aDepth,bool aIsShaped)30 WindowSurfaceX11Image::WindowSurfaceX11Image(Display* aDisplay, Window aWindow,
31 Visual* aVisual,
32 unsigned int aDepth,
33 bool aIsShaped)
34 : WindowSurfaceX11(aDisplay, aWindow, aVisual, aDepth),
35 mTransparencyBitmap(nullptr),
36 mTransparencyBitmapWidth(0),
37 mTransparencyBitmapHeight(0),
38 mIsShaped(aIsShaped) {}
39
~WindowSurfaceX11Image()40 WindowSurfaceX11Image::~WindowSurfaceX11Image() {
41 if (mTransparencyBitmap) {
42 delete[] mTransparencyBitmap;
43
44 Display* xDisplay = mWindowSurface->XDisplay();
45 Window xDrawable = mWindowSurface->XDrawable();
46
47 XShapeCombineMask(xDisplay, xDrawable, ShapeBounding, 0, 0, X11None,
48 ShapeSet);
49 }
50 }
51
Lock(const LayoutDeviceIntRegion & aRegion)52 already_AddRefed<gfx::DrawTarget> WindowSurfaceX11Image::Lock(
53 const LayoutDeviceIntRegion& aRegion) {
54 gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
55 gfx::IntSize size(bounds.XMost(), bounds.YMost());
56
57 if (!mWindowSurface || mWindowSurface->CairoStatus() ||
58 !(size <= mWindowSurface->GetSize())) {
59 mWindowSurface = new gfxXlibSurface(mDisplay, mWindow, mVisual, size);
60 }
61 if (mWindowSurface->CairoStatus()) {
62 return nullptr;
63 }
64
65 if (!mImageSurface || mImageSurface->CairoStatus() ||
66 !(size <= mImageSurface->GetSize())) {
67 gfxImageFormat format = SurfaceFormatToImageFormat(mFormat);
68 if (format == gfx::SurfaceFormat::UNKNOWN) {
69 format = mDepth == 32 ? gfx::SurfaceFormat::A8R8G8B8_UINT32
70 : gfx::SurfaceFormat::X8R8G8B8_UINT32;
71 }
72
73 // Use alpha image format for shaped window as we derive
74 // the shape bitmap from alpha channel. Must match SHAPED_IMAGE_SURFACE_BPP
75 // and SHAPED_IMAGE_SURFACE_ALPHA_INDEX.
76 if (mIsShaped) {
77 format = gfx::SurfaceFormat::A8R8G8B8_UINT32;
78 }
79
80 mImageSurface = new gfxImageSurface(size, format);
81 if (mImageSurface->CairoStatus()) {
82 return nullptr;
83 }
84 }
85
86 gfxImageFormat format = mImageSurface->Format();
87 // Cairo prefers compositing to BGRX instead of BGRA where possible.
88 // Cairo/pixman lacks some fast paths for compositing BGRX onto BGRA, so
89 // just report it as BGRX directly in that case.
90 // Otherwise, for Skia, report it as BGRA to the compositor. The alpha
91 // channel will be discarded when we put the image.
92 if (format == gfx::SurfaceFormat::X8R8G8B8_UINT32) {
93 gfx::BackendType backend = gfxVars::ContentBackend();
94 if (!gfx::Factory::DoesBackendSupportDataDrawtarget(backend)) {
95 #ifdef USE_SKIA
96 backend = gfx::BackendType::SKIA;
97 #else
98 backend = gfx::BackendType::CAIRO;
99 #endif
100 }
101 if (backend != gfx::BackendType::CAIRO) {
102 format = gfx::SurfaceFormat::A8R8G8B8_UINT32;
103 }
104 }
105
106 return gfxPlatform::CreateDrawTargetForData(
107 mImageSurface->Data(), mImageSurface->GetSize(), mImageSurface->Stride(),
108 ImageFormatToSurfaceFormat(format));
109 }
110
111 // The transparency bitmap routines are derived form the ones at nsWindow.cpp.
112 // The difference here is that we compose to RGBA image and then create
113 // the shape mask from final image alpha channel.
GetBitmapStride(int32_t width)114 static inline int32_t GetBitmapStride(int32_t width) { return (width + 7) / 8; }
115
ChangedMaskBits(gchar * aMaskBits,int32_t aMaskWidth,int32_t aMaskHeight,const nsIntRect & aRect,uint8_t * aImageData)116 static bool ChangedMaskBits(gchar* aMaskBits, int32_t aMaskWidth,
117 int32_t aMaskHeight, const nsIntRect& aRect,
118 uint8_t* aImageData) {
119 int32_t stride = aMaskWidth * SHAPED_IMAGE_SURFACE_BPP;
120 int32_t x, y, xMax = aRect.XMost(), yMax = aRect.YMost();
121 int32_t maskBytesPerRow = GetBitmapStride(aMaskWidth);
122 for (y = aRect.y; y < yMax; y++) {
123 gchar* maskBytes = aMaskBits + y * maskBytesPerRow;
124 uint8_t* alphas = aImageData;
125 for (x = aRect.x; x < xMax; x++) {
126 bool newBit = *(alphas + SHAPED_IMAGE_SURFACE_ALPHA_INDEX) > 0x7f;
127 alphas += SHAPED_IMAGE_SURFACE_BPP;
128
129 gchar maskByte = maskBytes[x >> 3];
130 bool maskBit = (maskByte & (1 << (x & 7))) != 0;
131
132 if (maskBit != newBit) {
133 return true;
134 }
135 }
136 aImageData += stride;
137 }
138
139 return false;
140 }
141
UpdateMaskBits(gchar * aMaskBits,int32_t aMaskWidth,int32_t aMaskHeight,const nsIntRect & aRect,uint8_t * aImageData)142 static void UpdateMaskBits(gchar* aMaskBits, int32_t aMaskWidth,
143 int32_t aMaskHeight, const nsIntRect& aRect,
144 uint8_t* aImageData) {
145 int32_t stride = aMaskWidth * SHAPED_IMAGE_SURFACE_BPP;
146 int32_t x, y, xMax = aRect.XMost(), yMax = aRect.YMost();
147 int32_t maskBytesPerRow = GetBitmapStride(aMaskWidth);
148 for (y = aRect.y; y < yMax; y++) {
149 gchar* maskBytes = aMaskBits + y * maskBytesPerRow;
150 uint8_t* alphas = aImageData;
151 for (x = aRect.x; x < xMax; x++) {
152 bool newBit = *(alphas + SHAPED_IMAGE_SURFACE_ALPHA_INDEX) > 0x7f;
153 alphas += SHAPED_IMAGE_SURFACE_BPP;
154
155 gchar mask = 1 << (x & 7);
156 gchar maskByte = maskBytes[x >> 3];
157 // Note: '-newBit' turns 0 into 00...00 and 1 into 11...11
158 maskBytes[x >> 3] = (maskByte & ~mask) | (-newBit & mask);
159 }
160 aImageData += stride;
161 }
162 }
163
ResizeTransparencyBitmap(int aWidth,int aHeight)164 void WindowSurfaceX11Image::ResizeTransparencyBitmap(int aWidth, int aHeight) {
165 int32_t actualSize =
166 GetBitmapStride(mTransparencyBitmapWidth) * mTransparencyBitmapHeight;
167 int32_t newSize = GetBitmapStride(aWidth) * aHeight;
168
169 if (actualSize < newSize) {
170 delete[] mTransparencyBitmap;
171 mTransparencyBitmap = new gchar[newSize];
172 }
173
174 mTransparencyBitmapWidth = aWidth;
175 mTransparencyBitmapHeight = aHeight;
176 }
177
ApplyTransparencyBitmap()178 void WindowSurfaceX11Image::ApplyTransparencyBitmap() {
179 gfx::IntSize size = mWindowSurface->GetSize();
180 bool maskChanged = true;
181
182 if (!mTransparencyBitmap) {
183 mTransparencyBitmapWidth = size.width;
184 mTransparencyBitmapHeight = size.height;
185
186 int32_t byteSize =
187 GetBitmapStride(mTransparencyBitmapWidth) * mTransparencyBitmapHeight;
188 mTransparencyBitmap = new gchar[byteSize];
189 } else {
190 bool sizeChanged = (size.width != mTransparencyBitmapWidth ||
191 size.height != mTransparencyBitmapHeight);
192
193 if (sizeChanged) {
194 ResizeTransparencyBitmap(size.width, size.height);
195 } else {
196 maskChanged = ChangedMaskBits(
197 mTransparencyBitmap, mTransparencyBitmapWidth,
198 mTransparencyBitmapHeight, nsIntRect(0, 0, size.width, size.height),
199 (uint8_t*)mImageSurface->Data());
200 }
201 }
202
203 if (maskChanged) {
204 UpdateMaskBits(mTransparencyBitmap, mTransparencyBitmapWidth,
205 mTransparencyBitmapHeight,
206 nsIntRect(0, 0, size.width, size.height),
207 (uint8_t*)mImageSurface->Data());
208
209 // We use X11 calls where possible, because GDK handles expose events
210 // for shaped windows in a way that's incompatible with us (Bug 635903).
211 // It doesn't occur when the shapes are set through X.
212 Display* xDisplay = mWindowSurface->XDisplay();
213 Window xDrawable = mWindowSurface->XDrawable();
214 Pixmap maskPixmap = XCreateBitmapFromData(
215 xDisplay, xDrawable, mTransparencyBitmap, mTransparencyBitmapWidth,
216 mTransparencyBitmapHeight);
217 XShapeCombineMask(xDisplay, xDrawable, ShapeBounding, 0, 0, maskPixmap,
218 ShapeSet);
219 XFreePixmap(xDisplay, maskPixmap);
220 }
221 }
222
Commit(const LayoutDeviceIntRegion & aInvalidRegion)223 void WindowSurfaceX11Image::Commit(
224 const LayoutDeviceIntRegion& aInvalidRegion) {
225 RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateDrawTargetForCairoSurface(
226 mWindowSurface->CairoSurface(), mWindowSurface->GetSize());
227 RefPtr<gfx::SourceSurface> surf =
228 gfx::Factory::CreateSourceSurfaceForCairoSurface(
229 mImageSurface->CairoSurface(), mImageSurface->GetSize(),
230 mImageSurface->Format());
231 if (!dt || !surf) {
232 return;
233 }
234
235 gfx::IntRect bounds = aInvalidRegion.GetBounds().ToUnknownRect();
236 gfx::Rect rect(bounds);
237 if (rect.IsEmpty()) {
238 return;
239 }
240
241 uint32_t numRects = aInvalidRegion.GetNumRects();
242 if (numRects != 1) {
243 AutoTArray<IntRect, 32> rects;
244 rects.SetCapacity(numRects);
245 for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
246 rects.AppendElement(iter.Get().ToUnknownRect());
247 }
248 dt->PushDeviceSpaceClipRects(rects.Elements(), rects.Length());
249 }
250
251 if (mIsShaped) {
252 ApplyTransparencyBitmap();
253 }
254
255 dt->DrawSurface(surf, rect, rect);
256
257 if (numRects != 1) {
258 dt->PopClip();
259 }
260 }
261
262 } // namespace widget
263 } // namespace mozilla
264