Backed out 2 changesets (bug 1855992) for causing talos failures @ mozilla::net:...
[gecko.git] / widget / gtk / WindowSurfaceX11Image.cpp
blobf83b64336cc2defe1b22189b28e6737ca0e57c6e
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) {}
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()) {
56 return nullptr;
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.
70 if (mIsShaped) {
71 format = gfx::SurfaceFormat::A8R8G8B8_UINT32;
74 mImageSurface = new gfxImageSurface(size, format);
75 if (mImageSurface->CairoStatus()) {
76 return nullptr;
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) {
123 return true;
126 aImageData += stride;
129 return false;
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) {
155 int32_t actualSize =
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;
176 int32_t byteSize =
177 GetBitmapStride(mTransparencyBitmapWidth) * mTransparencyBitmapHeight;
178 mTransparencyBitmap = new gchar[byteSize];
179 } else {
180 bool sizeChanged = (size.width != mTransparencyBitmapWidth ||
181 size.height != mTransparencyBitmapHeight);
183 if (sizeChanged) {
184 ResizeTransparencyBitmap(size.width, size.height);
185 } else {
186 maskChanged = ChangedMaskBits(
187 mTransparencyBitmap, mTransparencyBitmapWidth,
188 mTransparencyBitmapHeight, nsIntRect(0, 0, size.width, size.height),
189 (uint8_t*)mImageSurface->Data());
193 if (maskChanged) {
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,
208 ShapeSet);
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());
221 if (!dt || !surf) {
222 return;
225 gfx::IntRect bounds = aInvalidRegion.GetBounds().ToUnknownRect();
226 if (bounds.IsEmpty()) {
227 return;
230 if (mIsShaped) {
231 ApplyTransparencyBitmap();
234 uint32_t numRects = aInvalidRegion.GetNumRects();
235 if (numRects == 1) {
236 dt->CopySurface(surf, bounds, bounds.TopLeft());
237 } else {
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));
249 dt->PopClip();
253 } // namespace widget
254 } // namespace mozilla