forked from mirrors/gecko-dev
		
	 daab1d49e3
			
		
	
	
		daab1d49e3
		
	
	
	
	
		
			
			It has been unsupported since bug 1323303, > 4 years ago. This removes MOZ_ENABLE_SKIA but keeps USE_SKIA for moz2d for now Differential Revision: https://phabricator.services.mozilla.com/D120933
		
			
				
	
	
		
			259 lines
		
	
	
	
		
			8.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			259 lines
		
	
	
	
		
			8.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 | |
|  *
 | |
|  * This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "WindowSurfaceX11Image.h"
 | |
| 
 | |
| #include "mozilla/gfx/2D.h"
 | |
| #include "mozilla/gfx/Tools.h"
 | |
| #include "mozilla/gfx/gfxVars.h"
 | |
| #include "gfxPlatform.h"
 | |
| #include "gfx2DGlue.h"
 | |
| 
 | |
| #include <X11/extensions/shape.h>
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace widget {
 | |
| 
 | |
| using namespace mozilla::gfx;
 | |
| 
 | |
| // gfxImageSurface pixel format configuration.
 | |
| #define SHAPED_IMAGE_SURFACE_BPP 4
 | |
| #ifdef IS_BIG_ENDIAN
 | |
| #  define SHAPED_IMAGE_SURFACE_ALPHA_INDEX 0
 | |
| #else
 | |
| #  define SHAPED_IMAGE_SURFACE_ALPHA_INDEX 3
 | |
| #endif
 | |
| 
 | |
| WindowSurfaceX11Image::WindowSurfaceX11Image(Display* aDisplay, Window aWindow,
 | |
|                                              Visual* aVisual,
 | |
|                                              unsigned int aDepth,
 | |
|                                              bool aIsShaped)
 | |
|     : WindowSurfaceX11(aDisplay, aWindow, aVisual, aDepth),
 | |
|       mTransparencyBitmap(nullptr),
 | |
|       mTransparencyBitmapWidth(0),
 | |
|       mTransparencyBitmapHeight(0),
 | |
|       mIsShaped(aIsShaped) {}
 | |
| 
 | |
| WindowSurfaceX11Image::~WindowSurfaceX11Image() {
 | |
|   if (mTransparencyBitmap) {
 | |
|     delete[] mTransparencyBitmap;
 | |
| 
 | |
|     Display* xDisplay = mWindowSurface->XDisplay();
 | |
|     Window xDrawable = mWindowSurface->XDrawable();
 | |
| 
 | |
|     XShapeCombineMask(xDisplay, xDrawable, ShapeBounding, 0, 0, X11None,
 | |
|                       ShapeSet);
 | |
|   }
 | |
| }
 | |
| 
 | |
| already_AddRefed<gfx::DrawTarget> WindowSurfaceX11Image::Lock(
 | |
|     const LayoutDeviceIntRegion& aRegion) {
 | |
|   gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
 | |
|   gfx::IntSize size(bounds.XMost(), bounds.YMost());
 | |
| 
 | |
|   if (!mWindowSurface || mWindowSurface->CairoStatus() ||
 | |
|       !(size <= mWindowSurface->GetSize())) {
 | |
|     mWindowSurface = new gfxXlibSurface(mDisplay, mWindow, mVisual, size);
 | |
|   }
 | |
|   if (mWindowSurface->CairoStatus()) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   if (!mImageSurface || mImageSurface->CairoStatus() ||
 | |
|       !(size <= mImageSurface->GetSize())) {
 | |
|     gfxImageFormat format = SurfaceFormatToImageFormat(mFormat);
 | |
|     if (format == gfx::SurfaceFormat::UNKNOWN) {
 | |
|       format = mDepth == 32 ? gfx::SurfaceFormat::A8R8G8B8_UINT32
 | |
|                             : gfx::SurfaceFormat::X8R8G8B8_UINT32;
 | |
|     }
 | |
| 
 | |
|     // Use alpha image format for shaped window as we derive
 | |
|     // the shape bitmap from alpha channel. Must match SHAPED_IMAGE_SURFACE_BPP
 | |
|     // and SHAPED_IMAGE_SURFACE_ALPHA_INDEX.
 | |
|     if (mIsShaped) {
 | |
|       format = gfx::SurfaceFormat::A8R8G8B8_UINT32;
 | |
|     }
 | |
| 
 | |
|     mImageSurface = new gfxImageSurface(size, format);
 | |
|     if (mImageSurface->CairoStatus()) {
 | |
|       return nullptr;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   gfxImageFormat format = mImageSurface->Format();
 | |
|   // Cairo prefers compositing to BGRX instead of BGRA where possible.
 | |
|   // Cairo/pixman lacks some fast paths for compositing BGRX onto BGRA, so
 | |
|   // just report it as BGRX directly in that case.
 | |
|   // Otherwise, for Skia, report it as BGRA to the compositor. The alpha
 | |
|   // channel will be discarded when we put the image.
 | |
|   if (format == gfx::SurfaceFormat::X8R8G8B8_UINT32) {
 | |
|     gfx::BackendType backend = gfxVars::ContentBackend();
 | |
|     if (!gfx::Factory::DoesBackendSupportDataDrawtarget(backend)) {
 | |
|       backend = gfx::BackendType::SKIA;
 | |
|     }
 | |
|     if (backend != gfx::BackendType::CAIRO) {
 | |
|       format = gfx::SurfaceFormat::A8R8G8B8_UINT32;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return gfxPlatform::CreateDrawTargetForData(
 | |
|       mImageSurface->Data(), mImageSurface->GetSize(), mImageSurface->Stride(),
 | |
|       ImageFormatToSurfaceFormat(format));
 | |
| }
 | |
| 
 | |
| // The transparency bitmap routines are derived form the ones at nsWindow.cpp.
 | |
| // The difference here is that we compose to RGBA image and then create
 | |
| // the shape mask from final image alpha channel.
 | |
| static inline int32_t GetBitmapStride(int32_t width) { return (width + 7) / 8; }
 | |
| 
 | |
| static bool ChangedMaskBits(gchar* aMaskBits, int32_t aMaskWidth,
 | |
|                             int32_t aMaskHeight, const nsIntRect& aRect,
 | |
|                             uint8_t* aImageData) {
 | |
|   int32_t stride = aMaskWidth * SHAPED_IMAGE_SURFACE_BPP;
 | |
|   int32_t x, y, xMax = aRect.XMost(), yMax = aRect.YMost();
 | |
|   int32_t maskBytesPerRow = GetBitmapStride(aMaskWidth);
 | |
|   for (y = aRect.y; y < yMax; y++) {
 | |
|     gchar* maskBytes = aMaskBits + y * maskBytesPerRow;
 | |
|     uint8_t* alphas = aImageData;
 | |
|     for (x = aRect.x; x < xMax; x++) {
 | |
|       bool newBit = *(alphas + SHAPED_IMAGE_SURFACE_ALPHA_INDEX) > 0x7f;
 | |
|       alphas += SHAPED_IMAGE_SURFACE_BPP;
 | |
| 
 | |
|       gchar maskByte = maskBytes[x >> 3];
 | |
|       bool maskBit = (maskByte & (1 << (x & 7))) != 0;
 | |
| 
 | |
|       if (maskBit != newBit) {
 | |
|         return true;
 | |
|       }
 | |
|     }
 | |
|     aImageData += stride;
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| static void UpdateMaskBits(gchar* aMaskBits, int32_t aMaskWidth,
 | |
|                            int32_t aMaskHeight, const nsIntRect& aRect,
 | |
|                            uint8_t* aImageData) {
 | |
|   int32_t stride = aMaskWidth * SHAPED_IMAGE_SURFACE_BPP;
 | |
|   int32_t x, y, xMax = aRect.XMost(), yMax = aRect.YMost();
 | |
|   int32_t maskBytesPerRow = GetBitmapStride(aMaskWidth);
 | |
|   for (y = aRect.y; y < yMax; y++) {
 | |
|     gchar* maskBytes = aMaskBits + y * maskBytesPerRow;
 | |
|     uint8_t* alphas = aImageData;
 | |
|     for (x = aRect.x; x < xMax; x++) {
 | |
|       bool newBit = *(alphas + SHAPED_IMAGE_SURFACE_ALPHA_INDEX) > 0x7f;
 | |
|       alphas += SHAPED_IMAGE_SURFACE_BPP;
 | |
| 
 | |
|       gchar mask = 1 << (x & 7);
 | |
|       gchar maskByte = maskBytes[x >> 3];
 | |
|       // Note: '-newBit' turns 0 into 00...00 and 1 into 11...11
 | |
|       maskBytes[x >> 3] = (maskByte & ~mask) | (-newBit & mask);
 | |
|     }
 | |
|     aImageData += stride;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void WindowSurfaceX11Image::ResizeTransparencyBitmap(int aWidth, int aHeight) {
 | |
|   int32_t actualSize =
 | |
|       GetBitmapStride(mTransparencyBitmapWidth) * mTransparencyBitmapHeight;
 | |
|   int32_t newSize = GetBitmapStride(aWidth) * aHeight;
 | |
| 
 | |
|   if (actualSize < newSize) {
 | |
|     delete[] mTransparencyBitmap;
 | |
|     mTransparencyBitmap = new gchar[newSize];
 | |
|   }
 | |
| 
 | |
|   mTransparencyBitmapWidth = aWidth;
 | |
|   mTransparencyBitmapHeight = aHeight;
 | |
| }
 | |
| 
 | |
| void WindowSurfaceX11Image::ApplyTransparencyBitmap() {
 | |
|   gfx::IntSize size = mWindowSurface->GetSize();
 | |
|   bool maskChanged = true;
 | |
| 
 | |
|   if (!mTransparencyBitmap) {
 | |
|     mTransparencyBitmapWidth = size.width;
 | |
|     mTransparencyBitmapHeight = size.height;
 | |
| 
 | |
|     int32_t byteSize =
 | |
|         GetBitmapStride(mTransparencyBitmapWidth) * mTransparencyBitmapHeight;
 | |
|     mTransparencyBitmap = new gchar[byteSize];
 | |
|   } else {
 | |
|     bool sizeChanged = (size.width != mTransparencyBitmapWidth ||
 | |
|                         size.height != mTransparencyBitmapHeight);
 | |
| 
 | |
|     if (sizeChanged) {
 | |
|       ResizeTransparencyBitmap(size.width, size.height);
 | |
|     } else {
 | |
|       maskChanged = ChangedMaskBits(
 | |
|           mTransparencyBitmap, mTransparencyBitmapWidth,
 | |
|           mTransparencyBitmapHeight, nsIntRect(0, 0, size.width, size.height),
 | |
|           (uint8_t*)mImageSurface->Data());
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (maskChanged) {
 | |
|     UpdateMaskBits(mTransparencyBitmap, mTransparencyBitmapWidth,
 | |
|                    mTransparencyBitmapHeight,
 | |
|                    nsIntRect(0, 0, size.width, size.height),
 | |
|                    (uint8_t*)mImageSurface->Data());
 | |
| 
 | |
|     // We use X11 calls where possible, because GDK handles expose events
 | |
|     // for shaped windows in a way that's incompatible with us (Bug 635903).
 | |
|     // It doesn't occur when the shapes are set through X.
 | |
|     Display* xDisplay = mWindowSurface->XDisplay();
 | |
|     Window xDrawable = mWindowSurface->XDrawable();
 | |
|     Pixmap maskPixmap = XCreateBitmapFromData(
 | |
|         xDisplay, xDrawable, mTransparencyBitmap, mTransparencyBitmapWidth,
 | |
|         mTransparencyBitmapHeight);
 | |
|     XShapeCombineMask(xDisplay, xDrawable, ShapeBounding, 0, 0, maskPixmap,
 | |
|                       ShapeSet);
 | |
|     XFreePixmap(xDisplay, maskPixmap);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void WindowSurfaceX11Image::Commit(
 | |
|     const LayoutDeviceIntRegion& aInvalidRegion) {
 | |
|   RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateDrawTargetForCairoSurface(
 | |
|       mWindowSurface->CairoSurface(), mWindowSurface->GetSize());
 | |
|   RefPtr<gfx::SourceSurface> surf =
 | |
|       gfx::Factory::CreateSourceSurfaceForCairoSurface(
 | |
|           mImageSurface->CairoSurface(), mImageSurface->GetSize(),
 | |
|           mImageSurface->Format());
 | |
|   if (!dt || !surf) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   gfx::IntRect bounds = aInvalidRegion.GetBounds().ToUnknownRect();
 | |
|   gfx::Rect rect(bounds);
 | |
|   if (rect.IsEmpty()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   uint32_t numRects = aInvalidRegion.GetNumRects();
 | |
|   if (numRects != 1) {
 | |
|     AutoTArray<IntRect, 32> rects;
 | |
|     rects.SetCapacity(numRects);
 | |
|     for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
 | |
|       rects.AppendElement(iter.Get().ToUnknownRect());
 | |
|     }
 | |
|     dt->PushDeviceSpaceClipRects(rects.Elements(), rects.Length());
 | |
|   }
 | |
| 
 | |
|   if (mIsShaped) {
 | |
|     ApplyTransparencyBitmap();
 | |
|   }
 | |
| 
 | |
|   dt->DrawSurface(surf, rect, rect);
 | |
| 
 | |
|   if (numRects != 1) {
 | |
|     dt->PopClip();
 | |
|   }
 | |
| }
 | |
| 
 | |
| }  // namespace widget
 | |
| }  // namespace mozilla
 |