Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / widget / gtk / WindowSurfaceX11Image.cpp
blobf797bfafad273d7cf079d0d26b2c56afac51844b
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>
17 namespace mozilla {
18 namespace widget {
20 using namespace mozilla::gfx;
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
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 mWindowParent(0) {
40 if (!mIsShaped) {
41 return;
44 Window root, *children = nullptr;
45 unsigned int childrenNum;
46 if (XQueryTree(mDisplay, mWindow, &root, &mWindowParent, &children,
47 &childrenNum)) {
48 if (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()) {
70 return nullptr;
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.
84 if (mIsShaped) {
85 format = gfx::SurfaceFormat::A8R8G8B8_UINT32;
88 mImageSurface = new gfxImageSurface(size, format);
89 if (mImageSurface->CairoStatus()) {
90 return nullptr;
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) {
137 return true;
140 aImageData += stride;
143 return false;
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) {
169 int32_t actualSize =
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;
190 int32_t byteSize =
191 GetBitmapStride(mTransparencyBitmapWidth) * mTransparencyBitmapHeight;
192 mTransparencyBitmap = new gchar[byteSize];
193 } else {
194 bool sizeChanged = (size.width != mTransparencyBitmapWidth ||
195 size.height != mTransparencyBitmapHeight);
197 if (sizeChanged) {
198 ResizeTransparencyBitmap(size.width, size.height);
199 } else {
200 maskChanged = ChangedMaskBits(
201 mTransparencyBitmap, mTransparencyBitmapWidth,
202 mTransparencyBitmapHeight, nsIntRect(0, 0, size.width, size.height),
203 (uint8_t*)mImageSurface->Data());
207 if (maskChanged) {
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,
222 ShapeSet);
223 if (mWindowParent) {
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());
239 if (!dt || !surf) {
240 return;
243 gfx::IntRect bounds = aInvalidRegion.GetBounds().ToUnknownRect();
244 if (bounds.IsEmpty()) {
245 return;
248 if (mIsShaped) {
249 ApplyTransparencyBitmap();
252 uint32_t numRects = aInvalidRegion.GetNumRects();
253 if (numRects == 1) {
254 dt->CopySurface(surf, bounds, bounds.TopLeft());
255 } else {
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));
267 dt->PopClip();
271 } // namespace widget
272 } // namespace mozilla