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