Coverage Report

Created: 2018-09-25 14:53

/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