summaryrefslogtreecommitdiff
path: root/system/graphics/layers/basic
diff options
context:
space:
mode:
authorMoonchild <moonchild@palemoon.org>2022-02-12 17:47:03 +0000
committerMatt A. Tobin <email@mattatobin.com>2022-02-12 14:23:18 -0600
commitf66babd8b8368ada3e5aa29cdef1c77291ee4ddd (patch)
treee3842e2a6bf19090185f9c475b3846e1bb79ac97 /system/graphics/layers/basic
downloadGRE-f66babd8b8368ada3e5aa29cdef1c77291ee4ddd.tar.gz
Create the Goanna Runtime Environment
Diffstat (limited to 'system/graphics/layers/basic')
-rw-r--r--system/graphics/layers/basic/AutoMaskData.h60
-rw-r--r--system/graphics/layers/basic/BasicCanvasLayer.cpp141
-rw-r--r--system/graphics/layers/basic/BasicCanvasLayer.h51
-rw-r--r--system/graphics/layers/basic/BasicColorLayer.cpp83
-rw-r--r--system/graphics/layers/basic/BasicCompositor.cpp996
-rw-r--r--system/graphics/layers/basic/BasicCompositor.h162
-rw-r--r--system/graphics/layers/basic/BasicContainerLayer.cpp172
-rw-r--r--system/graphics/layers/basic/BasicContainerLayer.h106
-rw-r--r--system/graphics/layers/basic/BasicImageLayer.cpp119
-rw-r--r--system/graphics/layers/basic/BasicImages.cpp185
-rw-r--r--system/graphics/layers/basic/BasicImplData.h132
-rw-r--r--system/graphics/layers/basic/BasicLayerManager.cpp991
-rw-r--r--system/graphics/layers/basic/BasicLayers.h217
-rw-r--r--system/graphics/layers/basic/BasicLayersImpl.cpp211
-rw-r--r--system/graphics/layers/basic/BasicLayersImpl.h151
-rw-r--r--system/graphics/layers/basic/BasicPaintedLayer.cpp243
-rw-r--r--system/graphics/layers/basic/BasicPaintedLayer.h133
-rw-r--r--system/graphics/layers/basic/TextureClientX11.cpp156
-rw-r--r--system/graphics/layers/basic/TextureClientX11.h57
-rw-r--r--system/graphics/layers/basic/TextureHostBasic.cpp23
-rw-r--r--system/graphics/layers/basic/TextureHostBasic.h34
-rw-r--r--system/graphics/layers/basic/X11BasicCompositor.cpp136
-rw-r--r--system/graphics/layers/basic/X11BasicCompositor.h65
-rw-r--r--system/graphics/layers/basic/X11TextureSourceBasic.cpp67
-rw-r--r--system/graphics/layers/basic/X11TextureSourceBasic.h54
25 files changed, 4745 insertions, 0 deletions
diff --git a/system/graphics/layers/basic/AutoMaskData.h b/system/graphics/layers/basic/AutoMaskData.h
new file mode 100644
index 000000000..7bf7f9b3c
--- /dev/null
+++ b/system/graphics/layers/basic/AutoMaskData.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#ifndef GFX_AUTOMASKDATA_H_
+#define GFX_AUTOMASKDATA_H_
+
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Drawing with a mask requires a mask surface and a transform.
+ *
+ * This helper class manages the SourceSurface logic.
+ */
+class MOZ_STACK_CLASS AutoMoz2DMaskData {
+public:
+ AutoMoz2DMaskData() { }
+ ~AutoMoz2DMaskData() { }
+
+ void Construct(const gfx::Matrix& aTransform,
+ gfx::SourceSurface* aSurface)
+ {
+ MOZ_ASSERT(!IsConstructed());
+ mTransform = aTransform;
+ mSurface = aSurface;
+ }
+
+ gfx::SourceSurface* GetSurface()
+ {
+ MOZ_ASSERT(IsConstructed());
+ return mSurface.get();
+ }
+
+ const gfx::Matrix& GetTransform()
+ {
+ MOZ_ASSERT(IsConstructed());
+ return mTransform;
+ }
+
+private:
+ bool IsConstructed()
+ {
+ return !!mSurface;
+ }
+
+ gfx::Matrix mTransform;
+ RefPtr<gfx::SourceSurface> mSurface;
+
+ AutoMoz2DMaskData(const AutoMoz2DMaskData&) = delete;
+ AutoMoz2DMaskData& operator=(const AutoMoz2DMaskData&) = delete;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_AUTOMASKDATA_H_
diff --git a/system/graphics/layers/basic/BasicCanvasLayer.cpp b/system/graphics/layers/basic/BasicCanvasLayer.cpp
new file mode 100644
index 000000000..83c5c272e
--- /dev/null
+++ b/system/graphics/layers/basic/BasicCanvasLayer.cpp
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 2; 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 "BasicCanvasLayer.h"
+#include "AsyncCanvasRenderer.h"
+#include "basic/BasicLayers.h" // for BasicLayerManager
+#include "basic/BasicLayersImpl.h" // for GetEffectiveOperator
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "gfx2DGlue.h"
+#include "GLScreenBuffer.h"
+#include "GLContext.h"
+#include "gfxUtils.h"
+#include "mozilla/layers/PersistentBufferProvider.h"
+#include "client/TextureClientSharedSurface.h"
+
+class gfxContext;
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+already_AddRefed<SourceSurface>
+BasicCanvasLayer::UpdateSurface()
+{
+ if (mAsyncRenderer) {
+ MOZ_ASSERT(!mBufferProvider);
+ MOZ_ASSERT(!mGLContext);
+ return mAsyncRenderer->GetSurface();
+ }
+
+ if (!mGLContext) {
+ return nullptr;
+ }
+
+ SharedSurface* frontbuffer = nullptr;
+ if (mGLFrontbuffer) {
+ frontbuffer = mGLFrontbuffer.get();
+ } else {
+ GLScreenBuffer* screen = mGLContext->Screen();
+ const auto& front = screen->Front();
+ if (front) {
+ frontbuffer = front->Surf();
+ }
+ }
+
+ if (!frontbuffer) {
+ NS_WARNING("Null frame received.");
+ return nullptr;
+ }
+
+ IntSize readSize(frontbuffer->mSize);
+ SurfaceFormat format = (GetContentFlags() & CONTENT_OPAQUE)
+ ? SurfaceFormat::B8G8R8X8
+ : SurfaceFormat::B8G8R8A8;
+ bool needsPremult = frontbuffer->mHasAlpha && !mIsAlphaPremultiplied;
+
+ RefPtr<DataSourceSurface> resultSurf = GetTempSurface(readSize, format);
+ // There will already be a warning from inside of GetTempSurface, but
+ // it doesn't hurt to complain:
+ if (NS_WARN_IF(!resultSurf)) {
+ return nullptr;
+ }
+
+ // Readback handles Flush/MarkDirty.
+ mGLContext->Readback(frontbuffer, resultSurf);
+ if (needsPremult) {
+ gfxUtils::PremultiplyDataSurface(resultSurf, resultSurf);
+ }
+ MOZ_ASSERT(resultSurf);
+
+ return resultSurf.forget();
+}
+
+void
+BasicCanvasLayer::Paint(DrawTarget* aDT,
+ const Point& aDeviceOffset,
+ Layer* aMaskLayer)
+{
+ if (IsHidden())
+ return;
+
+ RefPtr<SourceSurface> surface;
+ if (IsDirty()) {
+ Painted();
+
+ FirePreTransactionCallback();
+ surface = UpdateSurface();
+ FireDidTransactionCallback();
+ }
+
+ bool bufferPoviderSnapshot = false;
+ if (!surface && mBufferProvider) {
+ surface = mBufferProvider->BorrowSnapshot();
+ bufferPoviderSnapshot = !!surface;
+ }
+
+ if (!surface) {
+ return;
+ }
+
+ const bool needsYFlip = (mOriginPos == gl::OriginPos::BottomLeft);
+
+ Matrix oldTM;
+ if (needsYFlip) {
+ oldTM = aDT->GetTransform();
+ aDT->SetTransform(Matrix(oldTM).
+ PreTranslate(0.0f, mBounds.height).
+ PreScale(1.0f, -1.0f));
+ }
+
+ FillRectWithMask(aDT, aDeviceOffset,
+ Rect(0, 0, mBounds.width, mBounds.height),
+ surface, mSamplingFilter,
+ DrawOptions(GetEffectiveOpacity(), GetEffectiveOperator(this)),
+ aMaskLayer);
+
+ if (needsYFlip) {
+ aDT->SetTransform(oldTM);
+ }
+
+ if (bufferPoviderSnapshot) {
+ mBufferProvider->ReturnSnapshot(surface.forget());
+ }
+}
+
+already_AddRefed<CanvasLayer>
+BasicLayerManager::CreateCanvasLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<CanvasLayer> layer = new BasicCanvasLayer(this);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/basic/BasicCanvasLayer.h b/system/graphics/layers/basic/BasicCanvasLayer.h
new file mode 100644
index 000000000..a63d2b8c0
--- /dev/null
+++ b/system/graphics/layers/basic/BasicCanvasLayer.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#ifndef GFX_BASICCANVASLAYER_H
+#define GFX_BASICCANVASLAYER_H
+
+#include "BasicImplData.h" // for BasicImplData
+#include "BasicLayers.h" // for BasicLayerManager
+#include "CopyableCanvasLayer.h" // for CopyableCanvasLayer
+#include "Layers.h" // for CanvasLayer, etc
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace layers {
+
+class BasicCanvasLayer : public CopyableCanvasLayer,
+ public BasicImplData
+{
+public:
+ explicit BasicCanvasLayer(BasicLayerManager* aLayerManager) :
+ CopyableCanvasLayer(aLayerManager, static_cast<BasicImplData*>(this))
+ { }
+
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ CanvasLayer::SetVisibleRegion(aRegion);
+ }
+
+ virtual void Paint(gfx::DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ Layer* aMaskLayer) override;
+
+protected:
+
+ already_AddRefed<gfx::SourceSurface> UpdateSurface();
+
+ BasicLayerManager* BasicManager()
+ {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/basic/BasicColorLayer.cpp b/system/graphics/layers/basic/BasicColorLayer.cpp
new file mode 100644
index 000000000..182bc785b
--- /dev/null
+++ b/system/graphics/layers/basic/BasicColorLayer.cpp
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 2; 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 "BasicLayersImpl.h" // for FillRectWithMask, etc
+#include "Layers.h" // for ColorLayer, etc
+#include "BasicImplData.h" // for BasicImplData
+#include "BasicLayers.h" // for BasicLayerManager
+#include "gfxContext.h" // for gfxContext, etc
+#include "gfxRect.h" // for gfxRect
+#include "gfx2DGlue.h"
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "mozilla/gfx/PathHelpers.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+class BasicColorLayer : public ColorLayer, public BasicImplData {
+public:
+ explicit BasicColorLayer(BasicLayerManager* aLayerManager) :
+ ColorLayer(aLayerManager, static_cast<BasicImplData*>(this))
+ {
+ MOZ_COUNT_CTOR(BasicColorLayer);
+ }
+
+protected:
+ virtual ~BasicColorLayer()
+ {
+ MOZ_COUNT_DTOR(BasicColorLayer);
+ }
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ColorLayer::SetVisibleRegion(aRegion);
+ }
+
+ virtual void Paint(DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ Layer* aMaskLayer) override
+ {
+ if (IsHidden()) {
+ return;
+ }
+
+ Rect snapped(mBounds.x, mBounds.y, mBounds.width, mBounds.height);
+ MaybeSnapToDevicePixels(snapped, *aDT, true);
+
+ // Clip drawing in case we're using (unbounded) operator source.
+ aDT->PushClipRect(snapped);
+ FillRectWithMask(aDT, aDeviceOffset, snapped, mColor,
+ DrawOptions(GetEffectiveOpacity(), GetEffectiveOperator(this)),
+ aMaskLayer);
+ aDT->PopClip();
+ }
+
+protected:
+ BasicLayerManager* BasicManager()
+ {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+};
+
+already_AddRefed<ColorLayer>
+BasicLayerManager::CreateColorLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ColorLayer> layer = new BasicColorLayer(this);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/basic/BasicCompositor.cpp b/system/graphics/layers/basic/BasicCompositor.cpp
new file mode 100644
index 000000000..634d9e340
--- /dev/null
+++ b/system/graphics/layers/basic/BasicCompositor.cpp
@@ -0,0 +1,996 @@
+/* -*- Mode: C++; tab-width: 20; 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 "BasicCompositor.h"
+#include "BasicLayersImpl.h" // for FillRectWithMask
+#include "TextureHostBasic.h"
+#include "mozilla/layers/Effects.h"
+#include "nsIWidget.h"
+#include "gfx2DGlue.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/Helpers.h"
+#include "mozilla/gfx/Tools.h"
+#include "mozilla/gfx/ssse3-scaler.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/SSE.h"
+#include "gfxUtils.h"
+#include "YCbCrUtils.h"
+#include <algorithm>
+#include "ImageContainer.h"
+#include "gfxPrefs.h"
+
+namespace mozilla {
+using namespace mozilla::gfx;
+
+namespace layers {
+
+class DataTextureSourceBasic : public DataTextureSource
+ , public TextureSourceBasic
+{
+public:
+ virtual const char* Name() const override { return "DataTextureSourceBasic"; }
+
+ explicit DataTextureSourceBasic(DataSourceSurface* aSurface)
+ : mSurface(aSurface)
+ , mWrappingExistingData(!!aSurface)
+ {}
+
+ virtual DataTextureSource* AsDataTextureSource() override
+ {
+ // If the texture wraps someone else's memory we'd rather not use it as
+ // a DataTextureSource per say (that is call Update on it).
+ return mWrappingExistingData ? nullptr : this;
+ }
+
+ virtual TextureSourceBasic* AsSourceBasic() override { return this; }
+
+ virtual gfx::SourceSurface* GetSurface(DrawTarget* aTarget) override { return mSurface; }
+
+ SurfaceFormat GetFormat() const override
+ {
+ return mSurface ? mSurface->GetFormat() : gfx::SurfaceFormat::UNKNOWN;
+ }
+
+ virtual IntSize GetSize() const override
+ {
+ return mSurface ? mSurface->GetSize() : gfx::IntSize(0, 0);
+ }
+
+ virtual bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr) override
+ {
+ MOZ_ASSERT(!mWrappingExistingData);
+ if (mWrappingExistingData) {
+ return false;
+ }
+ mSurface = aSurface;
+ return true;
+ }
+
+ virtual void DeallocateDeviceData() override
+ {
+ mSurface = nullptr;
+ SetUpdateSerial(0);
+ }
+
+public:
+ RefPtr<gfx::DataSourceSurface> mSurface;
+ bool mWrappingExistingData;
+};
+
+/**
+ * WrappingTextureSourceYCbCrBasic wraps YUV format BufferTextureHost to defer
+ * yuv->rgb conversion. The conversion happens when GetSurface is called.
+ */
+class WrappingTextureSourceYCbCrBasic : public DataTextureSource
+ , public TextureSourceBasic
+{
+public:
+ virtual const char* Name() const override { return "WrappingTextureSourceYCbCrBasic"; }
+
+ explicit WrappingTextureSourceYCbCrBasic(BufferTextureHost* aTexture)
+ : mTexture(aTexture)
+ , mSize(aTexture->GetSize())
+ , mNeedsUpdate(true)
+ {
+ mFromYCBCR = true;
+ }
+
+ virtual DataTextureSource* AsDataTextureSource() override
+ {
+ return this;
+ }
+
+ virtual TextureSourceBasic* AsSourceBasic() override { return this; }
+
+ virtual WrappingTextureSourceYCbCrBasic* AsWrappingTextureSourceYCbCrBasic() override { return this; }
+
+ virtual gfx::SourceSurface* GetSurface(DrawTarget* aTarget) override
+ {
+ if (mSurface && !mNeedsUpdate) {
+ return mSurface;
+ }
+ MOZ_ASSERT(mTexture);
+ if (!mTexture) {
+ return nullptr;
+ }
+
+ if (!mSurface) {
+ mSurface = Factory::CreateDataSourceSurface(mSize, gfx::SurfaceFormat::B8G8R8X8);
+ }
+ if (!mSurface) {
+ return nullptr;
+ }
+ MOZ_ASSERT(mTexture->GetBufferDescriptor().type() == BufferDescriptor::TYCbCrDescriptor);
+ MOZ_ASSERT(mTexture->GetSize() == mSize);
+
+ mSurface =
+ ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(
+ mTexture->GetBuffer(),
+ mTexture->GetBufferDescriptor().get_YCbCrDescriptor(),
+ mSurface);
+ mNeedsUpdate = false;
+ return mSurface;
+ }
+
+ SurfaceFormat GetFormat() const override
+ {
+ return gfx::SurfaceFormat::B8G8R8X8;
+ }
+
+ virtual IntSize GetSize() const override
+ {
+ return mSize;
+ }
+
+ virtual bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr) override
+ {
+ return false;
+ }
+
+ virtual void DeallocateDeviceData() override
+ {
+ mTexture = nullptr;
+ mSurface = nullptr;
+ SetUpdateSerial(0);
+ }
+
+ virtual void Unbind() override
+ {
+ mNeedsUpdate = true;
+ }
+
+ void SetBufferTextureHost(BufferTextureHost* aTexture) override
+ {
+ mTexture = aTexture;
+ mNeedsUpdate = true;
+ }
+
+ void ConvertAndScale(const SurfaceFormat& aDestFormat,
+ const IntSize& aDestSize,
+ unsigned char* aDestBuffer,
+ int32_t aStride)
+ {
+ MOZ_ASSERT(mTexture);
+ if (!mTexture) {
+ return;
+ }
+ MOZ_ASSERT(mTexture->GetBufferDescriptor().type() == BufferDescriptor::TYCbCrDescriptor);
+ MOZ_ASSERT(mTexture->GetSize() == mSize);
+ ImageDataSerializer::ConvertAndScaleFromYCbCrDescriptor(
+ mTexture->GetBuffer(),
+ mTexture->GetBufferDescriptor().get_YCbCrDescriptor(),
+ aDestFormat,
+ aDestSize,
+ aDestBuffer,
+ aStride);
+ }
+public:
+ BufferTextureHost* mTexture;
+ const gfx::IntSize mSize;
+ RefPtr<gfx::DataSourceSurface> mSurface;
+ bool mNeedsUpdate;
+};
+
+BasicCompositor::BasicCompositor(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget)
+ : Compositor(aWidget, aParent)
+ , mDidExternalComposition(false)
+ , mIsPendingEndRemoteDrawing(false)
+{
+ MOZ_COUNT_CTOR(BasicCompositor);
+
+ mMaxTextureSize = Factory::GetMaxSurfaceSize(gfxVars::ContentBackend());
+}
+
+BasicCompositor::~BasicCompositor()
+{
+ MOZ_COUNT_DTOR(BasicCompositor);
+}
+
+bool
+BasicCompositor::Initialize(nsCString* const out_failureReason)
+{
+ return mWidget ? mWidget->InitCompositor(this) : false;
+};
+
+int32_t
+BasicCompositor::GetMaxTextureSize() const
+{
+ return mMaxTextureSize;
+}
+
+void
+BasicCompositingRenderTarget::BindRenderTarget()
+{
+ if (mClearOnBind) {
+ mDrawTarget->ClearRect(Rect(0, 0, mSize.width, mSize.height));
+ mClearOnBind = false;
+ }
+}
+
+void BasicCompositor::DetachWidget()
+{
+ if (mWidget) {
+ if (mIsPendingEndRemoteDrawing) {
+ // Force to end previous remote drawing.
+ TryToEndRemoteDrawing(/* aForceToEnd */ true);
+ MOZ_ASSERT(!mIsPendingEndRemoteDrawing);
+ }
+ mWidget->CleanupRemoteDrawing();
+ }
+ Compositor::DetachWidget();
+}
+
+TextureFactoryIdentifier
+BasicCompositor::GetTextureFactoryIdentifier()
+{
+ TextureFactoryIdentifier ident(LayersBackend::LAYERS_BASIC,
+ XRE_GetProcessType(),
+ GetMaxTextureSize());
+ return ident;
+}
+
+already_AddRefed<CompositingRenderTarget>
+BasicCompositor::CreateRenderTarget(const IntRect& aRect, SurfaceInitMode aInit)
+{
+ MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size");
+
+ if (aRect.width * aRect.height == 0) {
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> target = mDrawTarget->CreateSimilarDrawTarget(aRect.Size(), SurfaceFormat::B8G8R8A8);
+
+ if (!target) {
+ return nullptr;
+ }
+
+ RefPtr<BasicCompositingRenderTarget> rt = new BasicCompositingRenderTarget(target, aRect);
+
+ return rt.forget();
+}
+
+already_AddRefed<CompositingRenderTarget>
+BasicCompositor::CreateRenderTargetFromSource(const IntRect &aRect,
+ const CompositingRenderTarget *aSource,
+ const IntPoint &aSourcePoint)
+{
+ MOZ_CRASH("GFX: Shouldn't be called!");
+ return nullptr;
+}
+
+already_AddRefed<CompositingRenderTarget>
+BasicCompositor::CreateRenderTargetForWindow(const LayoutDeviceIntRect& aRect, const LayoutDeviceIntRect& aClearRect, BufferMode aBufferMode)
+{
+ MOZ_ASSERT(mDrawTarget);
+ MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size");
+
+ if (aRect.width * aRect.height == 0) {
+ return nullptr;
+ }
+
+ RefPtr<BasicCompositingRenderTarget> rt;
+ IntRect rect = aRect.ToUnknownRect();
+
+ if (aBufferMode != BufferMode::BUFFER_NONE) {
+ RefPtr<DrawTarget> target = mWidget->GetBackBufferDrawTarget(mDrawTarget, aRect, aClearRect);
+ if (!target) {
+ return nullptr;
+ }
+ MOZ_ASSERT(target != mDrawTarget);
+ rt = new BasicCompositingRenderTarget(target, rect);
+ } else {
+ IntRect windowRect = rect;
+ // Adjust bounds rect to account for new origin at (0, 0).
+ if (windowRect.Size() != mDrawTarget->GetSize()) {
+ windowRect.ExpandToEnclose(IntPoint(0, 0));
+ }
+ rt = new BasicCompositingRenderTarget(mDrawTarget, windowRect);
+ if (!aClearRect.IsEmpty()) {
+ IntRect clearRect = aRect.ToUnknownRect();
+ mDrawTarget->ClearRect(Rect(clearRect - rt->GetOrigin()));
+ }
+ }
+
+ return rt.forget();
+}
+
+already_AddRefed<DataTextureSource>
+BasicCompositor::CreateDataTextureSource(TextureFlags aFlags)
+{
+ RefPtr<DataTextureSourceBasic> result = new DataTextureSourceBasic(nullptr);
+ if (aFlags & TextureFlags::RGB_FROM_YCBCR) {
+ result->mFromYCBCR = true;
+ }
+ return result.forget();
+}
+
+already_AddRefed<DataTextureSource>
+BasicCompositor::CreateDataTextureSourceAround(DataSourceSurface* aSurface)
+{
+ RefPtr<DataTextureSource> result = new DataTextureSourceBasic(aSurface);
+ return result.forget();
+}
+
+already_AddRefed<DataTextureSource>
+BasicCompositor::CreateDataTextureSourceAroundYCbCr(TextureHost* aTexture)
+{
+ BufferTextureHost* bufferTexture = aTexture->AsBufferTextureHost();
+ MOZ_ASSERT(bufferTexture);
+
+ if (!bufferTexture) {
+ return nullptr;
+ }
+ RefPtr<DataTextureSource> result = new WrappingTextureSourceYCbCrBasic(bufferTexture);
+ return result.forget();
+}
+
+bool
+BasicCompositor::SupportsEffect(EffectTypes aEffect)
+{
+ return aEffect != EffectTypes::YCBCR && aEffect != EffectTypes::COMPONENT_ALPHA;
+}
+
+static void
+DrawSurfaceWithTextureCoords(DrawTarget *aDest,
+ const gfx::Rect& aDestRect,
+ SourceSurface *aSource,
+ const gfx::Rect& aTextureCoords,
+ gfx::SamplingFilter aSamplingFilter,
+ const DrawOptions& aOptions,
+ SourceSurface *aMask,
+ const Matrix* aMaskTransform)
+{
+ if (!aSource) {
+ gfxWarning() << "DrawSurfaceWithTextureCoords problem " << gfx::hexa(aSource) << " and " << gfx::hexa(aMask);
+ return;
+ }
+
+ // Convert aTextureCoords into aSource's coordinate space
+ gfxRect sourceRect(aTextureCoords.x * aSource->GetSize().width,
+ aTextureCoords.y * aSource->GetSize().height,
+ aTextureCoords.width * aSource->GetSize().width,
+ aTextureCoords.height * aSource->GetSize().height);
+
+ // Floating point error can accumulate above and we know our visible region
+ // is integer-aligned, so round it out.
+ sourceRect.Round();
+
+ // Compute a transform that maps sourceRect to aDestRect.
+ Matrix matrix =
+ gfxUtils::TransformRectToRect(sourceRect,
+ gfx::IntPoint::Truncate(aDestRect.x, aDestRect.y),
+ gfx::IntPoint::Truncate(aDestRect.XMost(), aDestRect.y),
+ gfx::IntPoint::Truncate(aDestRect.XMost(), aDestRect.YMost()));
+
+ // Only use REPEAT if aTextureCoords is outside (0, 0, 1, 1).
+ gfx::Rect unitRect(0, 0, 1, 1);
+ ExtendMode mode = unitRect.Contains(aTextureCoords) ? ExtendMode::CLAMP : ExtendMode::REPEAT;
+
+ FillRectWithMask(aDest, aDestRect, aSource, aSamplingFilter, aOptions,
+ mode, aMask, aMaskTransform, &matrix);
+}
+
+static void
+SetupMask(const EffectChain& aEffectChain,
+ DrawTarget* aDest,
+ const IntPoint& aOffset,
+ RefPtr<SourceSurface>& aMaskSurface,
+ Matrix& aMaskTransform)
+{
+ if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) {
+ EffectMask *effectMask = static_cast<EffectMask*>(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get());
+ aMaskSurface = effectMask->mMaskTexture->AsSourceBasic()->GetSurface(aDest);
+ if (!aMaskSurface) {
+ gfxWarning() << "Invalid sourceMask effect";
+ }
+ MOZ_ASSERT(effectMask->mMaskTransform.Is2D(), "How did we end up with a 3D transform here?!");
+ aMaskTransform = effectMask->mMaskTransform.As2D();
+ aMaskTransform.PostTranslate(-aOffset.x, -aOffset.y);
+ }
+}
+
+static bool
+AttemptVideoScale(TextureSourceBasic* aSource, const SourceSurface* aSourceMask,
+ gfx::Float aOpacity, CompositionOp aBlendMode,
+ const TexturedEffect* aTexturedEffect,
+ const Matrix& aNewTransform, const gfx::Rect& aRect,
+ const gfx::Rect& aClipRect,
+ DrawTarget* aDest, const DrawTarget* aBuffer)
+{
+#ifdef MOZILLA_SSE_HAVE_CPUID_DETECTION
+ if (!mozilla::supports_ssse3())
+ return false;
+ if (aNewTransform.IsTranslation()) // unscaled painting should take the regular path
+ return false;
+ if (aNewTransform.HasNonAxisAlignedTransform() || aNewTransform.HasNegativeScaling())
+ return false;
+ if (aSourceMask || aOpacity != 1.0f)
+ return false;
+ if (aBlendMode != CompositionOp::OP_OVER && aBlendMode != CompositionOp::OP_SOURCE)
+ return false;
+
+ IntRect dstRect;
+ // the compiler should know a lot about aNewTransform at this point
+ // maybe it can do some sophisticated optimization of the following
+ if (!aNewTransform.TransformBounds(aRect).ToIntRect(&dstRect))
+ return false;
+
+ IntRect clipRect;
+ if (!aClipRect.ToIntRect(&clipRect))
+ return false;
+
+ if (!(aTexturedEffect->mTextureCoords == Rect(0.0f, 0.0f, 1.0f, 1.0f)))
+ return false;
+ if (aDest->GetFormat() == SurfaceFormat::R5G6B5_UINT16)
+ return false;
+
+ uint8_t* dstData;
+ IntSize dstSize;
+ int32_t dstStride;
+ SurfaceFormat dstFormat;
+ if (aDest->LockBits(&dstData, &dstSize, &dstStride, &dstFormat)) {
+ // If we're not painting to aBuffer the clip will
+ // be applied later
+ IntRect fillRect = dstRect;
+ if (aDest == aBuffer) {
+ // we need to clip fillRect because LockBits ignores the clip on the aDest
+ fillRect = fillRect.Intersect(clipRect);
+ }
+
+ fillRect = fillRect.Intersect(IntRect(IntPoint(0, 0), aDest->GetSize()));
+ IntPoint offset = fillRect.TopLeft() - dstRect.TopLeft();
+
+ RefPtr<DataSourceSurface> srcSource = aSource->GetSurface(aDest)->GetDataSurface();
+ DataSourceSurface::ScopedMap mapSrc(srcSource, DataSourceSurface::READ);
+
+ bool success = ssse3_scale_data((uint32_t*)mapSrc.GetData(), srcSource->GetSize().width, srcSource->GetSize().height,
+ mapSrc.GetStride()/4,
+ ((uint32_t*)dstData) + fillRect.x + (dstStride / 4) * fillRect.y, dstRect.width, dstRect.height,
+ dstStride / 4,
+ offset.x, offset.y,
+ fillRect.width, fillRect.height);
+
+ aDest->ReleaseBits(dstData);
+ return success;
+ } else
+#endif // MOZILLA_SSE_HAVE_CPUID_DETECTION
+ return false;
+}
+
+static bool
+AttemptVideoConvertAndScale(TextureSource* aSource, const SourceSurface* aSourceMask,
+ gfx::Float aOpacity, CompositionOp aBlendMode,
+ const TexturedEffect* aTexturedEffect,
+ const Matrix& aNewTransform, const gfx::Rect& aRect,
+ const gfx::Rect& aClipRect,
+ DrawTarget* aDest, const DrawTarget* aBuffer)
+{
+#if defined(XP_WIN) && defined(_M_X64)
+ // libyuv does not support SIMD scaling on win 64bit. See Bug 1295927.
+ return false;
+#endif
+
+ WrappingTextureSourceYCbCrBasic* wrappingSource = aSource->AsWrappingTextureSourceYCbCrBasic();
+ if (!wrappingSource)
+ return false;
+#ifdef MOZILLA_SSE_HAVE_CPUID_DETECTION
+ if (!mozilla::supports_ssse3()) // libyuv requests SSSE3 for fast YUV conversion.
+ return false;
+ if (aNewTransform.HasNonAxisAlignedTransform() || aNewTransform.HasNegativeScaling())
+ return false;
+ if (aSourceMask || aOpacity != 1.0f)
+ return false;
+ if (aBlendMode != CompositionOp::OP_OVER && aBlendMode != CompositionOp::OP_SOURCE)
+ return false;
+
+ IntRect dstRect;
+ // the compiler should know a lot about aNewTransform at this point
+ // maybe it can do some sophisticated optimization of the following
+ if (!aNewTransform.TransformBounds(aRect).ToIntRect(&dstRect))
+ return false;
+
+ IntRect clipRect;
+ if (!aClipRect.ToIntRect(&clipRect))
+ return false;
+
+ if (!(aTexturedEffect->mTextureCoords == Rect(0.0f, 0.0f, 1.0f, 1.0f)))
+ return false;
+ if (aDest->GetFormat() == SurfaceFormat::R5G6B5_UINT16)
+ return false;
+
+ if (aDest == aBuffer && !clipRect.Contains(dstRect))
+ return false;
+ if (!IntRect(IntPoint(0, 0), aDest->GetSize()).Contains(dstRect))
+ return false;
+
+ uint8_t* dstData;
+ IntSize dstSize;
+ int32_t dstStride;
+ SurfaceFormat dstFormat;
+ if (aDest->LockBits(&dstData, &dstSize, &dstStride, &dstFormat)) {
+ wrappingSource->ConvertAndScale(dstFormat,
+ dstRect.Size(),
+ dstData + ptrdiff_t(dstRect.x) * BytesPerPixel(dstFormat) + ptrdiff_t(dstRect.y) * dstStride,
+ dstStride);
+ aDest->ReleaseBits(dstData);
+ return true;
+ } else
+#endif // MOZILLA_SSE_HAVE_CPUID_DETECTION
+ return false;
+}
+
+void
+BasicCompositor::DrawQuad(const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain &aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect)
+{
+ RefPtr<DrawTarget> buffer = mRenderTarget->mDrawTarget;
+
+ // For 2D drawing, |dest| and |buffer| are the same surface. For 3D drawing,
+ // |dest| is a temporary surface.
+ RefPtr<DrawTarget> dest = buffer;
+
+ AutoRestoreTransform autoRestoreTransform(dest);
+
+ Matrix newTransform;
+ Rect transformBounds;
+ Matrix4x4 new3DTransform;
+ IntPoint offset = mRenderTarget->GetOrigin();
+
+ if (aTransform.Is2D()) {
+ newTransform = aTransform.As2D();
+ } else {
+ // Create a temporary surface for the transform.
+ dest = Factory::CreateDrawTarget(gfxVars::ContentBackend(), RoundedOut(aRect).Size(), SurfaceFormat::B8G8R8A8);
+ if (!dest) {
+ return;
+ }
+
+ dest->SetTransform(Matrix::Translation(-aRect.x, -aRect.y));
+
+ // Get the bounds post-transform.
+ transformBounds = aTransform.TransformAndClipBounds(aRect, Rect(offset.x, offset.y, buffer->GetSize().width, buffer->GetSize().height));
+ transformBounds.RoundOut();
+
+ if (transformBounds.IsEmpty()) {
+ return;
+ }
+
+ newTransform = Matrix();
+
+ // When we apply the 3D transformation, we do it against a temporary
+ // surface, so undo the coordinate offset.
+ new3DTransform = aTransform;
+ new3DTransform.PreTranslate(aRect.x, aRect.y, 0);
+ }
+
+ // XXX the transform is probably just an integer offset so this whole
+ // business here is a bit silly.
+ Rect transformedClipRect = buffer->GetTransform().TransformBounds(Rect(aClipRect));
+
+ buffer->PushClipRect(Rect(aClipRect));
+
+ newTransform.PostTranslate(-offset.x, -offset.y);
+ buffer->SetTransform(newTransform);
+
+ RefPtr<SourceSurface> sourceMask;
+ Matrix maskTransform;
+ if (aTransform.Is2D()) {
+ SetupMask(aEffectChain, dest, offset, sourceMask, maskTransform);
+ }
+
+ CompositionOp blendMode = CompositionOp::OP_OVER;
+ if (Effect* effect = aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get()) {
+ blendMode = static_cast<EffectBlendMode*>(effect)->mBlendMode;
+ }
+
+ switch (aEffectChain.mPrimaryEffect->mType) {
+ case EffectTypes::SOLID_COLOR: {
+ EffectSolidColor* effectSolidColor =
+ static_cast<EffectSolidColor*>(aEffectChain.mPrimaryEffect.get());
+
+ bool unboundedOp = !IsOperatorBoundByMask(blendMode);
+ if (unboundedOp) {
+ dest->PushClipRect(aRect);
+ }
+
+ FillRectWithMask(dest, aRect, effectSolidColor->mColor,
+ DrawOptions(aOpacity, blendMode), sourceMask, &maskTransform);
+
+ if (unboundedOp) {
+ dest->PopClip();
+ }
+ break;
+ }
+ case EffectTypes::RGB: {
+ TexturedEffect* texturedEffect =
+ static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
+ TextureSourceBasic* source = texturedEffect->mTexture->AsSourceBasic();
+
+ if (source && texturedEffect->mPremultiplied) {
+ // we have a fast path for video here
+ if (source->mFromYCBCR &&
+ AttemptVideoConvertAndScale(texturedEffect->mTexture, sourceMask, aOpacity, blendMode,
+ texturedEffect,
+ newTransform, aRect, transformedClipRect,
+ dest, buffer)) {
+ // we succeeded in convert and scaling
+ } else if (source->mFromYCBCR &&
+ !source->GetSurface(dest)) {
+ gfxWarning() << "Failed to get YCbCr to rgb surface.";
+ } else if (source->mFromYCBCR &&
+ AttemptVideoScale(source, sourceMask, aOpacity, blendMode,
+ texturedEffect,
+ newTransform, aRect, transformedClipRect,
+ dest, buffer)) {
+ // we succeeded in scaling
+ } else {
+ DrawSurfaceWithTextureCoords(dest, aRect,
+ source->GetSurface(dest),
+ texturedEffect->mTextureCoords,
+ texturedEffect->mSamplingFilter,
+ DrawOptions(aOpacity, blendMode),
+ sourceMask, &maskTransform);
+ }
+ } else if (source) {
+ SourceSurface* srcSurf = source->GetSurface(dest);
+ if (srcSurf) {
+ RefPtr<DataSourceSurface> srcData = srcSurf->GetDataSurface();
+
+ // Yes, we re-create the premultiplied data every time.
+ // This might be better with a cache, eventually.
+ RefPtr<DataSourceSurface> premultData = gfxUtils::CreatePremultipliedDataSurface(srcData);
+
+ DrawSurfaceWithTextureCoords(dest, aRect,
+ premultData,
+ texturedEffect->mTextureCoords,
+ texturedEffect->mSamplingFilter,
+ DrawOptions(aOpacity, blendMode),
+ sourceMask, &maskTransform);
+ }
+ } else {
+ gfxDevCrash(LogReason::IncompatibleBasicTexturedEffect) << "Bad for basic with " << texturedEffect->mTexture->Name() << " and " << gfx::hexa(sourceMask);
+ }
+
+ break;
+ }
+ case EffectTypes::YCBCR: {
+ NS_RUNTIMEABORT("Can't (easily) support component alpha with BasicCompositor!");
+ break;
+ }
+ case EffectTypes::RENDER_TARGET: {
+ EffectRenderTarget* effectRenderTarget =
+ static_cast<EffectRenderTarget*>(aEffectChain.mPrimaryEffect.get());
+ RefPtr<BasicCompositingRenderTarget> surface
+ = static_cast<BasicCompositingRenderTarget*>(effectRenderTarget->mRenderTarget.get());
+ RefPtr<SourceSurface> sourceSurf = surface->mDrawTarget->Snapshot();
+
+ DrawSurfaceWithTextureCoords(dest, aRect,
+ sourceSurf,
+ effectRenderTarget->mTextureCoords,
+ effectRenderTarget->mSamplingFilter,
+ DrawOptions(aOpacity, blendMode),
+ sourceMask, &maskTransform);
+ break;
+ }
+ case EffectTypes::COMPONENT_ALPHA: {
+ NS_RUNTIMEABORT("Can't (easily) support component alpha with BasicCompositor!");
+ break;
+ }
+ default: {
+ NS_RUNTIMEABORT("Invalid effect type!");
+ break;
+ }
+ }
+
+ if (!aTransform.Is2D()) {
+ dest->Flush();
+
+ RefPtr<SourceSurface> destSnapshot = dest->Snapshot();
+
+ SetupMask(aEffectChain, buffer, offset, sourceMask, maskTransform);
+
+ if (sourceMask) {
+ RefPtr<DrawTarget> transformDT =
+ dest->CreateSimilarDrawTarget(IntSize::Truncate(transformBounds.width, transformBounds.height),
+ SurfaceFormat::B8G8R8A8);
+ new3DTransform.PostTranslate(-transformBounds.x, -transformBounds.y, 0);
+ if (transformDT &&
+ transformDT->Draw3DTransformedSurface(destSnapshot, new3DTransform)) {
+ RefPtr<SourceSurface> transformSnapshot = transformDT->Snapshot();
+
+ // Transform the source by it's normal transform, and then the inverse
+ // of the mask transform so that it's in the mask's untransformed
+ // coordinate space.
+ Matrix sourceTransform = newTransform;
+ sourceTransform.PostTranslate(transformBounds.TopLeft());
+
+ Matrix inverseMask = maskTransform;
+ inverseMask.Invert();
+
+ sourceTransform *= inverseMask;
+
+ SurfacePattern source(transformSnapshot, ExtendMode::CLAMP, sourceTransform);
+
+ buffer->PushClipRect(transformBounds);
+
+ // Mask in the untransformed coordinate space, and then transform
+ // by the mask transform to put the result back into destination
+ // coords.
+ buffer->SetTransform(maskTransform);
+ buffer->MaskSurface(source, sourceMask, Point(0, 0));
+
+ buffer->PopClip();
+ }
+ } else {
+ buffer->Draw3DTransformedSurface(destSnapshot, new3DTransform);
+ }
+ }
+
+ buffer->PopClip();
+}
+
+void
+BasicCompositor::ClearRect(const gfx::Rect& aRect)
+{
+ mRenderTarget->mDrawTarget->ClearRect(aRect);
+}
+
+void
+BasicCompositor::BeginFrame(const nsIntRegion& aInvalidRegion,
+ const gfx::IntRect *aClipRectIn,
+ const gfx::IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion,
+ gfx::IntRect *aClipRectOut /* = nullptr */,
+ gfx::IntRect *aRenderBoundsOut /* = nullptr */)
+{
+ if (mIsPendingEndRemoteDrawing) {
+ // Force to end previous remote drawing.
+ TryToEndRemoteDrawing(/* aForceToEnd */ true);
+ MOZ_ASSERT(!mIsPendingEndRemoteDrawing);
+ }
+
+ LayoutDeviceIntRect intRect(LayoutDeviceIntPoint(), mWidget->GetClientSize());
+ IntRect rect = IntRect(0, 0, intRect.width, intRect.height);
+
+ LayoutDeviceIntRegion invalidRegionSafe;
+ if (mDidExternalComposition) {
+ // We do not know rendered region during external composition, just redraw
+ // whole widget.
+ invalidRegionSafe = intRect;
+ mDidExternalComposition = false;
+ } else {
+ // Sometimes the invalid region is larger than we want to draw.
+ invalidRegionSafe.And(
+ LayoutDeviceIntRegion::FromUnknownRegion(aInvalidRegion), intRect);
+ }
+
+ mInvalidRegion = invalidRegionSafe;
+ mInvalidRect = mInvalidRegion.GetBounds();
+
+ if (aRenderBoundsOut) {
+ *aRenderBoundsOut = IntRect();
+ }
+
+ BufferMode bufferMode = BufferMode::BUFFERED;
+ if (mTarget) {
+ // If we have a copy target, then we don't have a widget-provided mDrawTarget (currently). Use a dummy
+ // placeholder so that CreateRenderTarget() works. This is only used to create a new buffered
+ // draw target that we composite into, then copy the results the destination.
+ mDrawTarget = mTarget;
+ bufferMode = BufferMode::BUFFER_NONE;
+ } else {
+ // StartRemoteDrawingInRegion can mutate mInvalidRegion.
+ mDrawTarget = mWidget->StartRemoteDrawingInRegion(mInvalidRegion, &bufferMode);
+ if (!mDrawTarget) {
+ return;
+ }
+ mInvalidRect = mInvalidRegion.GetBounds();
+ if (mInvalidRect.IsEmpty()) {
+ mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion);
+ return;
+ }
+ }
+
+ if (!mDrawTarget || mInvalidRect.IsEmpty()) {
+ return;
+ }
+
+ LayoutDeviceIntRect clearRect;
+ if (!aOpaqueRegion.IsEmpty()) {
+ LayoutDeviceIntRegion clearRegion = mInvalidRegion;
+ clearRegion.SubOut(LayoutDeviceIntRegion::FromUnknownRegion(aOpaqueRegion));
+ clearRect = clearRegion.GetBounds();
+ } else {
+ clearRect = mInvalidRect;
+ }
+
+ // Prevent CreateRenderTargetForWindow from clearing unwanted area.
+ gfxUtils::ClipToRegion(mDrawTarget,
+ mInvalidRegion.ToUnknownRegion());
+
+ // Setup an intermediate render target to buffer all compositing. We will
+ // copy this into mDrawTarget (the widget), and/or mTarget in EndFrame()
+ RefPtr<CompositingRenderTarget> target =
+ CreateRenderTargetForWindow(mInvalidRect,
+ clearRect,
+ bufferMode);
+
+ mDrawTarget->PopClip();
+
+ if (!target) {
+ if (!mTarget) {
+ mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion);
+ }
+ return;
+ }
+ SetRenderTarget(target);
+
+ // We only allocate a surface sized to the invalidated region, so we need to
+ // translate future coordinates.
+ mRenderTarget->mDrawTarget->SetTransform(Matrix::Translation(-mRenderTarget->GetOrigin()));
+
+ gfxUtils::ClipToRegion(mRenderTarget->mDrawTarget,
+ mInvalidRegion.ToUnknownRegion());
+
+ if (aRenderBoundsOut) {
+ *aRenderBoundsOut = rect;
+ }
+
+ if (aClipRectIn) {
+ mRenderTarget->mDrawTarget->PushClipRect(Rect(*aClipRectIn));
+ } else {
+ mRenderTarget->mDrawTarget->PushClipRect(Rect(rect));
+ if (aClipRectOut) {
+ *aClipRectOut = rect;
+ }
+ }
+}
+
+void
+BasicCompositor::EndFrame()
+{
+ Compositor::EndFrame();
+
+ // Pop aClipRectIn/bounds rect
+ mRenderTarget->mDrawTarget->PopClip();
+
+ if (gfxPrefs::WidgetUpdateFlashing()) {
+ float r = float(rand()) / RAND_MAX;
+ float g = float(rand()) / RAND_MAX;
+ float b = float(rand()) / RAND_MAX;
+ // We're still clipped to mInvalidRegion, so just fill the bounds.
+ mRenderTarget->mDrawTarget->FillRect(
+ IntRectToRect(mInvalidRegion.GetBounds()).ToUnknownRect(),
+ ColorPattern(Color(r, g, b, 0.2f)));
+ }
+
+ // Pop aInvalidregion
+ mRenderTarget->mDrawTarget->PopClip();
+
+ TryToEndRemoteDrawing();
+}
+
+void
+BasicCompositor::TryToEndRemoteDrawing(bool aForceToEnd)
+{
+ if (mIsDestroyed || !mRenderTarget) {
+ return;
+ }
+
+ // It it is not a good timing for EndRemoteDrawing, defter to call it.
+ if (!aForceToEnd && !mTarget && NeedsToDeferEndRemoteDrawing()) {
+ mIsPendingEndRemoteDrawing = true;
+
+ const uint32_t retryMs = 2;
+ RefPtr<BasicCompositor> self = this;
+ RefPtr<Runnable> runnable = NS_NewRunnableFunction([self]() {
+ self->TryToEndRemoteDrawing();
+ });
+ MessageLoop::current()->PostDelayedTask(runnable.forget(), retryMs);
+ return;
+ }
+
+ if (mRenderTarget->mDrawTarget != mDrawTarget) {
+ // Note: Most platforms require us to buffer drawing to the widget surface.
+ // That's why we don't draw to mDrawTarget directly.
+ RefPtr<SourceSurface> source;
+ if (mRenderTarget->mDrawTarget != mDrawTarget) {
+ source = mWidget->EndBackBufferDrawing();
+ } else {
+ source = mRenderTarget->mDrawTarget->Snapshot();
+ }
+ RefPtr<DrawTarget> dest(mTarget ? mTarget : mDrawTarget);
+
+ nsIntPoint offset = mTarget ? mTargetBounds.TopLeft() : nsIntPoint();
+
+ // The source DrawTarget is clipped to the invalidation region, so we have
+ // to copy the individual rectangles in the region or else we'll draw blank
+ // pixels.
+ for (auto iter = mInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const LayoutDeviceIntRect& r = iter.Get();
+ dest->CopySurface(source,
+ IntRect(r.x, r.y, r.width, r.height) - mRenderTarget->GetOrigin(),
+ IntPoint(r.x, r.y) - offset);
+ }
+ }
+
+ if (aForceToEnd || !mTarget) {
+ mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion);
+ }
+
+ mDrawTarget = nullptr;
+ mRenderTarget = nullptr;
+ mIsPendingEndRemoteDrawing = false;
+}
+
+bool
+BasicCompositor::NeedsToDeferEndRemoteDrawing()
+{
+ MOZ_ASSERT(mDrawTarget);
+ MOZ_ASSERT(mRenderTarget);
+
+ if (mTarget || mRenderTarget->mDrawTarget == mDrawTarget) {
+ return false;
+ }
+
+ return mWidget->NeedsToDeferEndRemoteDrawing();
+}
+
+void
+BasicCompositor::FinishPendingComposite()
+{
+ TryToEndRemoteDrawing(/* aForceToEnd */ true);
+}
+
+void
+BasicCompositor::EndFrameForExternalComposition(const gfx::Matrix& aTransform)
+{
+ MOZ_ASSERT(!mTarget);
+ MOZ_ASSERT(!mDrawTarget);
+ MOZ_ASSERT(!mRenderTarget);
+
+ mDidExternalComposition = true;
+}
+
+BasicCompositor*
+AssertBasicCompositor(Compositor* aCompositor)
+{
+ BasicCompositor* compositor = aCompositor ? aCompositor->AsBasicCompositor()
+ : nullptr;
+ MOZ_DIAGNOSTIC_ASSERT(!!compositor);
+ return compositor;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/basic/BasicCompositor.h b/system/graphics/layers/basic/BasicCompositor.h
new file mode 100644
index 000000000..73f3e82c3
--- /dev/null
+++ b/system/graphics/layers/basic/BasicCompositor.h
@@ -0,0 +1,162 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#ifndef MOZILLA_GFX_BASICCOMPOSITOR_H
+#define MOZILLA_GFX_BASICCOMPOSITOR_H
+
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+class BasicCompositingRenderTarget : public CompositingRenderTarget
+{
+public:
+ BasicCompositingRenderTarget(gfx::DrawTarget* aDrawTarget, const gfx::IntRect& aRect)
+ : CompositingRenderTarget(aRect.TopLeft())
+ , mDrawTarget(aDrawTarget)
+ , mSize(aRect.Size())
+ { }
+
+ virtual const char* Name() const override { return "BasicCompositingRenderTarget"; }
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ void BindRenderTarget();
+
+ virtual gfx::SurfaceFormat GetFormat() const override
+ {
+ return mDrawTarget ? mDrawTarget->GetFormat()
+ : gfx::SurfaceFormat(gfx::SurfaceFormat::UNKNOWN);
+ }
+
+ RefPtr<gfx::DrawTarget> mDrawTarget;
+ gfx::IntSize mSize;
+};
+
+class BasicCompositor : public Compositor
+{
+public:
+ explicit BasicCompositor(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget);
+
+protected:
+ virtual ~BasicCompositor();
+
+public:
+
+ virtual BasicCompositor* AsBasicCompositor() override { return this; }
+
+ virtual bool Initialize(nsCString* const out_failureReason) override;
+
+ virtual void DetachWidget() override;
+
+ virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() override;
+
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTarget(const gfx::IntRect &aRect, SurfaceInitMode aInit) override;
+
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTargetFromSource(const gfx::IntRect &aRect,
+ const CompositingRenderTarget *aSource,
+ const gfx::IntPoint &aSourcePoint) override;
+
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTargetForWindow(const LayoutDeviceIntRect& aRect,
+ const LayoutDeviceIntRect& aClearRect,
+ BufferMode aBufferMode);
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) override;
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSourceAroundYCbCr(TextureHost* aTexture) override;
+
+ virtual bool SupportsEffect(EffectTypes aEffect) override;
+
+ virtual void SetRenderTarget(CompositingRenderTarget *aSource) override
+ {
+ mRenderTarget = static_cast<BasicCompositingRenderTarget*>(aSource);
+ mRenderTarget->BindRenderTarget();
+ }
+ virtual CompositingRenderTarget* GetCurrentRenderTarget() const override
+ {
+ return mRenderTarget;
+ }
+
+ virtual void DrawQuad(const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain &aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect) override;
+
+ virtual void ClearRect(const gfx::Rect& aRect) override;
+
+ virtual void BeginFrame(const nsIntRegion& aInvalidRegion,
+ const gfx::IntRect *aClipRectIn,
+ const gfx::IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion,
+ gfx::IntRect *aClipRectOut = nullptr,
+ gfx::IntRect *aRenderBoundsOut = nullptr) override;
+ virtual void EndFrame() override;
+ virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) override;
+
+ virtual bool SupportsPartialTextureUpdate() override { return true; }
+ virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) override { return true; }
+ virtual int32_t GetMaxTextureSize() const override;
+ virtual void SetDestinationSurfaceSize(const gfx::IntSize& aSize) override { }
+
+ virtual void SetScreenRenderOffset(const ScreenPoint& aOffset) override {
+ }
+
+ virtual void MakeCurrent(MakeCurrentFlags aFlags = 0) override { }
+
+#ifdef MOZ_DUMP_PAINTING
+ virtual const char* Name() const override { return "Basic"; }
+#endif // MOZ_DUMP_PAINTING
+
+ virtual LayersBackend GetBackendType() const override {
+ return LayersBackend::LAYERS_BASIC;
+ }
+
+ gfx::DrawTarget *GetDrawTarget() { return mDrawTarget; }
+
+ virtual bool IsPendingComposite() override
+ {
+ return mIsPendingEndRemoteDrawing;
+ }
+
+ virtual void FinishPendingComposite() override;
+
+private:
+
+ void TryToEndRemoteDrawing(bool aForceToEnd = false);
+
+ bool NeedsToDeferEndRemoteDrawing();
+
+ // The final destination surface
+ RefPtr<gfx::DrawTarget> mDrawTarget;
+ // The current render target for drawing
+ RefPtr<BasicCompositingRenderTarget> mRenderTarget;
+
+ LayoutDeviceIntRect mInvalidRect;
+ LayoutDeviceIntRegion mInvalidRegion;
+ bool mDidExternalComposition;
+
+ uint32_t mMaxTextureSize;
+ bool mIsPendingEndRemoteDrawing;
+};
+
+BasicCompositor* AssertBasicCompositor(Compositor* aCompositor);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_BASICCOMPOSITOR_H */
diff --git a/system/graphics/layers/basic/BasicContainerLayer.cpp b/system/graphics/layers/basic/BasicContainerLayer.cpp
new file mode 100644
index 000000000..499e202c4
--- /dev/null
+++ b/system/graphics/layers/basic/BasicContainerLayer.cpp
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 2; 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 "BasicContainerLayer.h"
+#include <sys/types.h> // for int32_t
+#include "BasicLayersImpl.h" // for ToData
+#include "basic/BasicImplData.h" // for BasicImplData
+#include "basic/BasicLayers.h" // for BasicLayerManager
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRegion.h" // for nsIntRegion
+#include "ReadbackProcessor.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+BasicContainerLayer::~BasicContainerLayer()
+{
+ while (mFirstChild) {
+ ContainerLayer::RemoveChild(mFirstChild);
+ }
+
+ MOZ_COUNT_DTOR(BasicContainerLayer);
+}
+
+void
+BasicContainerLayer::ComputeEffectiveTransforms(const Matrix4x4& aTransformToSurface)
+{
+ // We push groups for container layers if we need to, which always
+ // are aligned in device space, so it doesn't really matter how we snap
+ // containers.
+ Matrix residual;
+ Matrix4x4 transformToSurface = aTransformToSurface;
+ bool participate3DCtx = Extend3DContext() || Is3DContextLeaf();
+ if (!participate3DCtx &&
+ GetContentFlags() & CONTENT_BACKFACE_HIDDEN) {
+ // For backface-hidden layers
+ transformToSurface.ProjectTo2D();
+ }
+ Matrix4x4 idealTransform = GetLocalTransform() * transformToSurface;
+ if (!participate3DCtx &&
+ !(GetContentFlags() & CONTENT_BACKFACE_HIDDEN)) {
+ // For non-backface-hidden layers,
+ // 3D components are required to handle CONTENT_BACKFACE_HIDDEN.
+ idealTransform.ProjectTo2D();
+ }
+
+ if (!idealTransform.CanDraw2D()) {
+ if (!Extend3DContext()) {
+ mEffectiveTransform = idealTransform;
+ ComputeEffectiveTransformsForChildren(Matrix4x4());
+ ComputeEffectiveTransformForMaskLayers(Matrix4x4());
+ mUseIntermediateSurface = true;
+ return;
+ }
+
+ mEffectiveTransform = idealTransform;
+ ComputeEffectiveTransformsForChildren(idealTransform);
+ ComputeEffectiveTransformForMaskLayers(idealTransform);
+ mUseIntermediateSurface = false;
+ return;
+ }
+
+ // With 2D transform or extended 3D context.
+
+ Layer* child = GetFirstChild();
+ bool hasSingleBlendingChild = false;
+ if (!HasMultipleChildren() && child) {
+ hasSingleBlendingChild = child->GetMixBlendMode() != CompositionOp::OP_OVER;
+ }
+
+ /* If we have a single childand it is not blending,, it can just inherit our opacity,
+ * otherwise we need a PushGroup and we need to mark ourselves as using
+ * an intermediate surface so our children don't inherit our opacity
+ * via GetEffectiveOpacity.
+ * Having a mask layer always forces our own push group
+ * Having a blend mode also always forces our own push group
+ */
+ mUseIntermediateSurface =
+ GetMaskLayer() ||
+ GetForceIsolatedGroup() ||
+ (GetMixBlendMode() != CompositionOp::OP_OVER && HasMultipleChildren()) ||
+ (GetEffectiveOpacity() != 1.0 && ((HasMultipleChildren() && !Extend3DContext()) || hasSingleBlendingChild));
+
+ mEffectiveTransform =
+ !mUseIntermediateSurface ?
+ idealTransform :
+ (!(GetContentFlags() & CONTENT_BACKFACE_HIDDEN) ?
+ SnapTransformTranslation(idealTransform, &residual) :
+ SnapTransformTranslation3D(idealTransform, &residual));
+ Matrix4x4 childTransformToSurface =
+ (!mUseIntermediateSurface ||
+ (mUseIntermediateSurface && !Extend3DContext() /* 2D */)) ?
+ idealTransform : Matrix4x4::From2D(residual);
+ ComputeEffectiveTransformsForChildren(childTransformToSurface);
+
+ ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+}
+
+bool
+BasicContainerLayer::ChildrenPartitionVisibleRegion(const gfx::IntRect& aInRect)
+{
+ Matrix transform;
+ if (!GetEffectiveTransform().CanDraw2D(&transform) ||
+ ThebesMatrix(transform).HasNonIntegerTranslation())
+ return false;
+
+ nsIntPoint offset(int32_t(transform._31), int32_t(transform._32));
+ gfx::IntRect rect = aInRect.Intersect(GetLocalVisibleRegion().ToUnknownRegion().GetBounds() + offset);
+ nsIntRegion covered;
+
+ for (Layer* l = mFirstChild; l; l = l->GetNextSibling()) {
+ if (ToData(l)->IsHidden())
+ continue;
+
+ Matrix childTransform;
+ if (!l->GetEffectiveTransform().CanDraw2D(&childTransform) ||
+ ThebesMatrix(childTransform).HasNonIntegerTranslation() ||
+ l->GetEffectiveOpacity() != 1.0)
+ return false;
+ nsIntRegion childRegion = l->GetLocalVisibleRegion().ToUnknownRegion();
+ childRegion.MoveBy(int32_t(childTransform._31), int32_t(childTransform._32));
+ childRegion.And(childRegion, rect);
+ if (l->GetClipRect()) {
+ childRegion.And(childRegion, l->GetClipRect()->ToUnknownRect() + offset);
+ }
+ nsIntRegion intersection;
+ intersection.And(covered, childRegion);
+ if (!intersection.IsEmpty())
+ return false;
+ covered.Or(covered, childRegion);
+ }
+
+ return covered.Contains(rect);
+}
+
+void
+BasicContainerLayer::Validate(LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ ReadbackProcessor* aReadback)
+{
+ ReadbackProcessor readback;
+ if (BasicManager()->IsRetained()) {
+ readback.BuildUpdates(this);
+ }
+ for (Layer* l = mFirstChild; l; l = l->GetNextSibling()) {
+ BasicImplData* data = ToData(l);
+ data->Validate(aCallback, aCallbackData, &readback);
+ if (l->GetMaskLayer()) {
+ data = ToData(l->GetMaskLayer());
+ data->Validate(aCallback, aCallbackData, nullptr);
+ }
+ }
+}
+
+already_AddRefed<ContainerLayer>
+BasicLayerManager::CreateContainerLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ContainerLayer> layer = new BasicContainerLayer(this);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/basic/BasicContainerLayer.h b/system/graphics/layers/basic/BasicContainerLayer.h
new file mode 100644
index 000000000..c8227f6e9
--- /dev/null
+++ b/system/graphics/layers/basic/BasicContainerLayer.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#ifndef GFX_BASICCONTAINERLAYER_H
+#define GFX_BASICCONTAINERLAYER_H
+
+#include "BasicImplData.h" // for BasicImplData
+#include "BasicLayers.h" // for BasicLayerManager
+#include "Layers.h" // for Layer, ContainerLayer
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR
+#include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
+#include "mozilla/gfx/Rect.h"
+
+namespace mozilla {
+namespace layers {
+
+class BasicContainerLayer : public ContainerLayer, public BasicImplData {
+public:
+ explicit BasicContainerLayer(BasicLayerManager* aManager) :
+ ContainerLayer(aManager, static_cast<BasicImplData*>(this))
+ {
+ MOZ_COUNT_CTOR(BasicContainerLayer);
+ mSupportsComponentAlphaChildren = true;
+ }
+protected:
+ virtual ~BasicContainerLayer();
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ContainerLayer::SetVisibleRegion(aRegion);
+ }
+ virtual bool InsertAfter(Layer* aChild, Layer* aAfter) override
+ {
+ if (!BasicManager()->InConstruction()) {
+ NS_ERROR("Can only set properties in construction phase");
+ return false;
+ }
+ return ContainerLayer::InsertAfter(aChild, aAfter);
+ }
+
+ virtual bool RemoveChild(Layer* aChild) override
+ {
+ if (!BasicManager()->InConstruction()) {
+ NS_ERROR("Can only set properties in construction phase");
+ return false;
+ }
+ return ContainerLayer::RemoveChild(aChild);
+ }
+
+ virtual bool RepositionChild(Layer* aChild, Layer* aAfter) override
+ {
+ if (!BasicManager()->InConstruction()) {
+ NS_ERROR("Can only set properties in construction phase");
+ return false;
+ }
+ return ContainerLayer::RepositionChild(aChild, aAfter);
+ }
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override;
+
+ /**
+ * Returns true when:
+ * a) no (non-hidden) childrens' visible areas overlap in
+ * (aInRect intersected with this layer's visible region).
+ * b) the (non-hidden) childrens' visible areas cover
+ * (aInRect intersected with this layer's visible region).
+ * c) this layer and all (non-hidden) children have transforms that are translations
+ * by integers.
+ * aInRect is in the root coordinate system.
+ * Child layers with opacity do not contribute to the covered area in check b).
+ * This method can be conservative; it's OK to return false under any
+ * circumstances.
+ */
+ bool ChildrenPartitionVisibleRegion(const gfx::IntRect& aInRect);
+
+ void ForceIntermediateSurface() { mUseIntermediateSurface = true; }
+
+ void SetSupportsComponentAlphaChildren(bool aSupports) { mSupportsComponentAlphaChildren = aSupports; }
+
+ virtual void Validate(LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ ReadbackProcessor* aReadback) override;
+
+ /**
+ * We don't really have a hard restriction for max layer size, but we pick
+ * 4096 to avoid excessive memory usage.
+ */
+ virtual int32_t GetMaxLayerSize() override { return 4096; }
+
+protected:
+ BasicLayerManager* BasicManager()
+ {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/basic/BasicImageLayer.cpp b/system/graphics/layers/basic/BasicImageLayer.cpp
new file mode 100644
index 000000000..0bb6b132d
--- /dev/null
+++ b/system/graphics/layers/basic/BasicImageLayer.cpp
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 2; 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 "BasicLayersImpl.h" // for FillRectWithMask, etc
+#include "ImageContainer.h" // for AutoLockImage, etc
+#include "ImageLayers.h" // for ImageLayer
+#include "Layers.h" // for Layer (ptr only), etc
+#include "basic/BasicImplData.h" // for BasicImplData
+#include "basic/BasicLayers.h" // for BasicLayerManager
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for gfxPattern::Release, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "mozilla/gfx/Point.h" // for IntSize
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+class BasicImageLayer : public ImageLayer, public BasicImplData {
+public:
+ explicit BasicImageLayer(BasicLayerManager* aLayerManager) :
+ ImageLayer(aLayerManager, static_cast<BasicImplData*>(this)),
+ mSize(-1, -1)
+ {
+ MOZ_COUNT_CTOR(BasicImageLayer);
+ }
+protected:
+ virtual ~BasicImageLayer()
+ {
+ MOZ_COUNT_DTOR(BasicImageLayer);
+ }
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ImageLayer::SetVisibleRegion(aRegion);
+ }
+
+ virtual void Paint(DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ Layer* aMaskLayer) override;
+
+ virtual already_AddRefed<SourceSurface> GetAsSourceSurface() override;
+
+protected:
+ BasicLayerManager* BasicManager()
+ {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+
+ gfx::IntSize mSize;
+};
+
+void
+BasicImageLayer::Paint(DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ Layer* aMaskLayer)
+{
+ if (IsHidden() || !mContainer) {
+ return;
+ }
+
+ RefPtr<ImageFactory> originalIF = mContainer->GetImageFactory();
+ mContainer->SetImageFactory(mManager->IsCompositingCheap() ? nullptr : BasicManager()->GetImageFactory());
+
+ AutoLockImage autoLock(mContainer);
+ Image *image = autoLock.GetImage();
+ if (!image) {
+ mContainer->SetImageFactory(originalIF);
+ return;
+ }
+ RefPtr<gfx::SourceSurface> surface = image->GetAsSourceSurface();
+ if (!surface || !surface->IsValid()) {
+ mContainer->SetImageFactory(originalIF);
+ return;
+ }
+
+ gfx::IntSize size = mSize = surface->GetSize();
+ FillRectWithMask(aDT, aDeviceOffset, Rect(0, 0, size.width, size.height),
+ surface, mSamplingFilter,
+ DrawOptions(GetEffectiveOpacity(), GetEffectiveOperator(this)),
+ aMaskLayer);
+
+ mContainer->SetImageFactory(originalIF);
+}
+
+already_AddRefed<SourceSurface>
+BasicImageLayer::GetAsSourceSurface()
+{
+ if (!mContainer) {
+ return nullptr;
+ }
+
+ AutoLockImage lockImage(mContainer);
+ Image* image = lockImage.GetImage();
+ if (!image) {
+ return nullptr;
+ }
+ return image->GetAsSourceSurface();
+}
+
+already_AddRefed<ImageLayer>
+BasicLayerManager::CreateImageLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ImageLayer> layer = new BasicImageLayer(this);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/basic/BasicImages.cpp b/system/graphics/layers/basic/BasicImages.cpp
new file mode 100644
index 000000000..fc1be6e9a
--- /dev/null
+++ b/system/graphics/layers/basic/BasicImages.cpp
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 20; 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 <stdint.h> // for uint8_t, uint32_t
+#include "BasicLayers.h" // for BasicLayerManager
+#include "ImageContainer.h" // for PlanarYCbCrImage, etc
+#include "ImageTypes.h" // for ImageFormat, etc
+#include "cairo.h" // for cairo_user_data_key_t
+#include "gfxASurface.h" // for gfxASurface, etc
+#include "gfxPlatform.h" // for gfxPlatform, gfxImageFormat
+#include "gfxUtils.h" // for gfxUtils
+#include "mozilla/CheckedInt.h"
+#include "mozilla/mozalloc.h" // for operator delete[], etc
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "nsAutoRef.h" // for nsCountedRef
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ERROR, NS_ASSERTION
+#include "nsISupportsImpl.h" // for Image::Release, etc
+#include "nsThreadUtils.h" // for NS_IsMainThread
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "gfx2DGlue.h"
+#include "YCbCrUtils.h" // for YCbCr conversions
+
+namespace mozilla {
+namespace layers {
+
+class BasicPlanarYCbCrImage : public RecyclingPlanarYCbCrImage
+{
+public:
+ BasicPlanarYCbCrImage(const gfx::IntSize& aScaleHint, gfxImageFormat aOffscreenFormat, BufferRecycleBin *aRecycleBin)
+ : RecyclingPlanarYCbCrImage(aRecycleBin)
+ , mScaleHint(aScaleHint)
+ , mStride(0)
+ , mDelayedConversion(false)
+ {
+ SetOffscreenFormat(aOffscreenFormat);
+ }
+
+ ~BasicPlanarYCbCrImage()
+ {
+ if (mDecodedBuffer) {
+ // Right now this only happens if the Image was never drawn, otherwise
+ // this will have been tossed away at surface destruction.
+ mRecycleBin->RecycleBuffer(Move(mDecodedBuffer), mSize.height * mStride);
+ }
+ }
+
+ virtual bool CopyData(const Data& aData) override;
+ virtual void SetDelayedConversion(bool aDelayed) override { mDelayedConversion = aDelayed; }
+
+ already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+
+ virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
+ {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
+ {
+ size_t size = RecyclingPlanarYCbCrImage::SizeOfExcludingThis(aMallocSizeOf);
+ size += aMallocSizeOf(mDecodedBuffer.get());
+ return size;
+ }
+
+private:
+ UniquePtr<uint8_t[]> mDecodedBuffer;
+ gfx::IntSize mScaleHint;
+ int mStride;
+ bool mDelayedConversion;
+};
+
+class BasicImageFactory : public ImageFactory
+{
+public:
+ BasicImageFactory() {}
+
+ virtual RefPtr<PlanarYCbCrImage>
+ CreatePlanarYCbCrImage(const gfx::IntSize& aScaleHint, BufferRecycleBin* aRecycleBin)
+ {
+ return new BasicPlanarYCbCrImage(aScaleHint, gfxPlatform::GetPlatform()->GetOffscreenFormat(), aRecycleBin);
+ }
+};
+
+bool
+BasicPlanarYCbCrImage::CopyData(const Data& aData)
+{
+ RecyclingPlanarYCbCrImage::CopyData(aData);
+
+ if (mDelayedConversion) {
+ return false;
+ }
+
+ // Do some sanity checks to prevent integer overflow
+ if (aData.mYSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
+ aData.mYSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
+ NS_ERROR("Illegal image source width or height");
+ return false;
+ }
+
+ gfx::SurfaceFormat format = gfx::ImageFormatToSurfaceFormat(GetOffscreenFormat());
+
+ gfx::IntSize size(mScaleHint);
+ gfx::GetYCbCrToRGBDestFormatAndSize(aData, format, size);
+ if (size.width > PlanarYCbCrImage::MAX_DIMENSION ||
+ size.height > PlanarYCbCrImage::MAX_DIMENSION) {
+ NS_ERROR("Illegal image dest width or height");
+ return false;
+ }
+
+ gfxImageFormat iFormat = gfx::SurfaceFormatToImageFormat(format);
+ mStride = gfxASurface::FormatStrideForWidth(iFormat, size.width);
+ mozilla::CheckedInt32 requiredBytes =
+ mozilla::CheckedInt32(size.height) * mozilla::CheckedInt32(mStride);
+ if (!requiredBytes.isValid()) {
+ // invalid size
+ return false;
+ }
+ mDecodedBuffer = AllocateBuffer(requiredBytes.value());
+ if (!mDecodedBuffer) {
+ // out of memory
+ return false;
+ }
+
+ gfx::ConvertYCbCrToRGB(aData, format, size, mDecodedBuffer.get(), mStride);
+ SetOffscreenFormat(iFormat);
+ mSize = size;
+
+ return true;
+}
+
+already_AddRefed<gfx::SourceSurface>
+BasicPlanarYCbCrImage::GetAsSourceSurface()
+{
+ NS_ASSERTION(NS_IsMainThread(), "Must be main thread");
+
+ if (mSourceSurface) {
+ RefPtr<gfx::SourceSurface> surface(mSourceSurface);
+ return surface.forget();
+ }
+
+ if (!mDecodedBuffer) {
+ return PlanarYCbCrImage::GetAsSourceSurface();
+ }
+
+ gfxImageFormat format = GetOffscreenFormat();
+
+ RefPtr<gfx::SourceSurface> surface;
+ {
+ // Create a DrawTarget so that we can own the data inside mDecodeBuffer.
+ // We create the target out of mDecodedBuffer, and get a snapshot from it.
+ // The draw target is destroyed on scope exit and the surface owns the data.
+ RefPtr<gfx::DrawTarget> drawTarget
+ = gfxPlatform::CreateDrawTargetForData(mDecodedBuffer.get(),
+ mSize,
+ mStride,
+ gfx::ImageFormatToSurfaceFormat(format));
+ if (!drawTarget) {
+ return nullptr;
+ }
+
+ surface = drawTarget->Snapshot();
+ }
+
+ mRecycleBin->RecycleBuffer(Move(mDecodedBuffer), mSize.height * mStride);
+
+ mSourceSurface = surface;
+ return surface.forget();
+}
+
+
+ImageFactory*
+BasicLayerManager::GetImageFactory()
+{
+ if (!mFactory) {
+ mFactory = new BasicImageFactory();
+ }
+
+ return mFactory.get();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/basic/BasicImplData.h b/system/graphics/layers/basic/BasicImplData.h
new file mode 100644
index 000000000..2ffa310a9
--- /dev/null
+++ b/system/graphics/layers/basic/BasicImplData.h
@@ -0,0 +1,132 @@
+/* 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/. */
+
+#ifndef GFX_BASICIMPLDATA_H
+#define GFX_BASICIMPLDATA_H
+
+#include "Layers.h" // for Layer (ptr only), etc
+#include "gfxContext.h" // for gfxContext, etc
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "mozilla/gfx/Types.h"
+
+namespace mozilla {
+namespace layers {
+
+class ReadbackProcessor;
+
+/**
+ * This is the ImplData for all Basic layers. It also exposes methods
+ * private to the Basic implementation that are common to all Basic layer types.
+ * In particular, there is an internal Paint() method that we can use
+ * to paint the contents of non-PaintedLayers.
+ *
+ * The class hierarchy for Basic layers is like this:
+ * BasicImplData
+ * Layer | | |
+ * | | | |
+ * +-> ContainerLayer | | |
+ * | | | | |
+ * | +-> BasicContainerLayer <--+ | |
+ * | | |
+ * +-> PaintedLayer | |
+ * | | | |
+ * | +-> BasicPaintedLayer <---------+ |
+ * | |
+ * +-> ImageLayer |
+ * | |
+ * +-> BasicImageLayer <--------------+
+ */
+class BasicImplData {
+public:
+ BasicImplData() : mHidden(false),
+ mClipToVisibleRegion(false),
+ mDrawAtomically(false),
+ mOperator(gfx::CompositionOp::OP_OVER)
+ {
+ MOZ_COUNT_CTOR(BasicImplData);
+ }
+ virtual ~BasicImplData()
+ {
+ MOZ_COUNT_DTOR(BasicImplData);
+ }
+
+ /**
+ * Layers that paint themselves, such as ImageLayers, should paint
+ * in response to this method call. aContext will already have been
+ * set up to account for all the properties of the layer (transform,
+ * opacity, etc).
+ */
+ virtual void Paint(gfx::DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ Layer* aMaskLayer) {}
+
+ /**
+ * Like Paint() but called for PaintedLayers with the additional parameters
+ * they need.
+ * If mClipToVisibleRegion is set, then the layer must clip to its
+ * effective visible region (snapped or unsnapped, it doesn't matter).
+ */
+ virtual void PaintThebes(gfxContext* aContext,
+ Layer* aMasklayer,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData) {}
+
+ virtual void Validate(LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ ReadbackProcessor* aReadback) {}
+
+ /**
+ * Layers will get this call when their layer manager is destroyed, this
+ * indicates they should clear resources they don't really need after their
+ * LayerManager ceases to exist.
+ */
+ virtual void ClearCachedResources() {}
+
+ /**
+ * This variable is set by MarkLayersHidden() before painting. It indicates
+ * that the layer should not be composited during this transaction.
+ */
+ void SetHidden(bool aCovered) { mHidden = aCovered; }
+ bool IsHidden() const { return false; }
+ /**
+ * This variable is set by MarkLayersHidden() before painting. This is
+ * the operator to be used when compositing the layer in this transaction. It must
+ * be OVER or SOURCE.
+ */
+ void SetOperator(gfx::CompositionOp aOperator)
+ {
+ NS_ASSERTION(aOperator == gfx::CompositionOp::OP_OVER ||
+ aOperator == gfx::CompositionOp::OP_SOURCE,
+ "Bad composition operator");
+ mOperator = aOperator;
+ }
+
+ gfx::CompositionOp GetOperator() const { return mOperator; }
+
+ /**
+ * Return a surface for this layer. Will use an existing surface, if
+ * possible, or may create a temporary surface. Implement this
+ * method for any layers that might be used as a mask. Should only
+ * return false if a surface cannot be created. If true is
+ * returned, only one of |aSurface| or |aDescriptor| is valid.
+ */
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() { return nullptr; }
+
+ bool GetClipToVisibleRegion() { return mClipToVisibleRegion; }
+ void SetClipToVisibleRegion(bool aClip) { mClipToVisibleRegion = aClip; }
+
+ void SetDrawAtomically(bool aDrawAtomically) { mDrawAtomically = aDrawAtomically; }
+
+protected:
+ bool mHidden;
+ bool mClipToVisibleRegion;
+ bool mDrawAtomically;
+ gfx::CompositionOp mOperator;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/basic/BasicLayerManager.cpp b/system/graphics/layers/basic/BasicLayerManager.cpp
new file mode 100644
index 000000000..41c37dc8e
--- /dev/null
+++ b/system/graphics/layers/basic/BasicLayerManager.cpp
@@ -0,0 +1,991 @@
+/* -*- Mode: C++; tab-width: 2; 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 <stdint.h> // for uint32_t
+#include <stdlib.h> // for rand, RAND_MAX
+#include <sys/types.h> // for int32_t
+#include <stack> // for stack
+#include "BasicContainerLayer.h" // for BasicContainerLayer
+#include "BasicLayersImpl.h" // for ToData, BasicReadbackLayer, etc
+#include "GeckoProfiler.h" // for PROFILER_LABEL
+#include "ImageContainer.h" // for ImageFactory
+#include "Layers.h" // for Layer, ContainerLayer, etc
+#include "ReadbackLayer.h" // for ReadbackLayer
+#include "ReadbackProcessor.h" // for ReadbackProcessor
+#include "RenderTrace.h" // for RenderTraceLayers, etc
+#include "basic/BasicImplData.h" // for BasicImplData
+#include "basic/BasicLayers.h" // for BasicLayerManager, etc
+#include "gfxASurface.h" // for gfxASurface, etc
+#include "gfxContext.h" // for gfxContext, etc
+#include "gfxImageSurface.h" // for gfxImageSurface
+#include "gfxMatrix.h" // for gfxMatrix
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxPrefs.h" // for gfxPrefs
+#include "gfxPoint.h" // for IntSize, gfxPoint
+#include "gfxRect.h" // for gfxRect
+#include "gfxUtils.h" // for gfxUtils
+#include "gfx2DGlue.h" // for thebes --> moz2d transition
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/WidgetUtils.h" // for ScreenRotation
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/gfx/Matrix.h" // for Matrix
+#include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/gfx/Rect.h" // for IntRect, Rect
+#include "mozilla/layers/LayersTypes.h" // for BufferMode::BUFFER_NONE, etc
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsISupportsImpl.h" // for gfxContext::Release, etc
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion, etc
+#include "nsTArray.h" // for AutoTArray
+#include "TreeTraversal.h" // for ForEachNode
+
+class nsIWidget;
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+/**
+ * Clips to the smallest device-pixel-aligned rectangle containing aRect
+ * in user space.
+ * Returns true if the clip is "perfect", i.e. we actually clipped exactly to
+ * aRect.
+ */
+static bool
+ClipToContain(gfxContext* aContext, const IntRect& aRect)
+{
+ gfxRect userRect(aRect.x, aRect.y, aRect.width, aRect.height);
+ gfxRect deviceRect = aContext->UserToDevice(userRect);
+ deviceRect.RoundOut();
+
+ gfxMatrix currentMatrix = aContext->CurrentMatrix();
+ aContext->SetMatrix(gfxMatrix());
+ aContext->NewPath();
+ aContext->Rectangle(deviceRect);
+ aContext->Clip();
+ aContext->SetMatrix(currentMatrix);
+
+ return aContext->DeviceToUser(deviceRect).IsEqualInterior(userRect);
+}
+
+bool
+BasicLayerManager::PushGroupForLayer(gfxContext* aContext, Layer* aLayer, const nsIntRegion& aRegion, PushedGroup& aGroupResult)
+{
+ aGroupResult.mVisibleRegion = aRegion;
+ aGroupResult.mFinalTarget = aContext;
+ aGroupResult.mOperator = GetEffectiveOperator(aLayer);
+ aGroupResult.mOpacity = aLayer->GetEffectiveOpacity();
+
+ // If we need to call PushGroup, we should clip to the smallest possible
+ // area first to minimize the size of the temporary surface.
+ bool didCompleteClip = ClipToContain(aContext, aRegion.GetBounds());
+
+ bool canPushGroup = aGroupResult.mOperator == CompositionOp::OP_OVER ||
+ (aGroupResult.mOperator == CompositionOp::OP_SOURCE && (aLayer->CanUseOpaqueSurface() || aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA));
+
+ if (!canPushGroup) {
+ aContext->Save();
+ gfxUtils::ClipToRegion(aGroupResult.mFinalTarget, aGroupResult.mVisibleRegion);
+
+ // PushGroup/PopGroup do not support non operator over.
+ gfxMatrix oldMat = aContext->CurrentMatrix();
+ aContext->SetMatrix(gfxMatrix());
+ gfxRect rect = aContext->GetClipExtents();
+ aContext->SetMatrix(oldMat);
+ rect.RoundOut();
+ IntRect surfRect;
+ ToRect(rect).ToIntRect(&surfRect);
+
+ if (!surfRect.IsEmpty()) {
+ RefPtr<DrawTarget> dt = aContext->GetDrawTarget()->CreateSimilarDrawTarget(surfRect.Size(), SurfaceFormat::B8G8R8A8);
+
+ RefPtr<gfxContext> ctx =
+ gfxContext::CreateOrNull(dt, ToRect(rect).TopLeft());
+ if (!ctx) {
+ gfxCriticalNote << "BasicLayerManager context problem in PushGroupForLayer " << gfx::hexa(dt);
+ return false;
+ }
+ ctx->SetMatrix(oldMat);
+
+ aGroupResult.mGroupOffset = surfRect.TopLeft();
+ aGroupResult.mGroupTarget = ctx;
+
+ aGroupResult.mMaskSurface = GetMaskForLayer(aLayer, &aGroupResult.mMaskTransform);
+ return true;
+ }
+ aContext->Restore();
+ }
+
+ Matrix maskTransform;
+ RefPtr<SourceSurface> maskSurf = GetMaskForLayer(aLayer, &maskTransform);
+
+ if (maskSurf) {
+ // The returned transform will transform the mask to device space on the
+ // destination. Since the User->Device space transform will be applied
+ // to the mask by PopGroupAndBlend we need to adjust the transform to
+ // transform the mask to user space.
+ Matrix currentTransform = ToMatrix(aGroupResult.mFinalTarget->CurrentMatrix());
+ currentTransform.Invert();
+ maskTransform = maskTransform * currentTransform;
+ }
+
+ if (aLayer->CanUseOpaqueSurface() &&
+ ((didCompleteClip && aRegion.GetNumRects() == 1) ||
+ !aContext->CurrentMatrix().HasNonIntegerTranslation())) {
+ // If the layer is opaque in its visible region we can push a gfxContentType::COLOR
+ // group. We need to make sure that only pixels inside the layer's visible
+ // region are copied back to the destination. Remember if we've already
+ // clipped precisely to the visible region.
+ aGroupResult.mNeedsClipToVisibleRegion = !didCompleteClip || aRegion.GetNumRects() > 1;
+ if (aGroupResult.mNeedsClipToVisibleRegion) {
+ aGroupResult.mFinalTarget->Save();
+ gfxUtils::ClipToRegion(aGroupResult.mFinalTarget, aGroupResult.mVisibleRegion);
+ }
+
+ aContext->PushGroupForBlendBack(gfxContentType::COLOR, aGroupResult.mOpacity, maskSurf, maskTransform);
+ } else {
+ if (aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) {
+ aContext->PushGroupAndCopyBackground(gfxContentType::COLOR_ALPHA, aGroupResult.mOpacity, maskSurf, maskTransform);
+ } else {
+ aContext->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, aGroupResult.mOpacity, maskSurf, maskTransform);
+ }
+ }
+
+ aGroupResult.mGroupTarget = aGroupResult.mFinalTarget;
+
+ return true;
+}
+
+void
+BasicLayerManager::PopGroupForLayer(PushedGroup &group)
+{
+ if (group.mFinalTarget == group.mGroupTarget) {
+ group.mFinalTarget->PopGroupAndBlend();
+ if (group.mNeedsClipToVisibleRegion) {
+ group.mFinalTarget->Restore();
+ }
+ return;
+ }
+
+ DrawTarget* dt = group.mFinalTarget->GetDrawTarget();
+ RefPtr<DrawTarget> sourceDT = group.mGroupTarget->GetDrawTarget();
+ group.mGroupTarget = nullptr;
+
+ RefPtr<SourceSurface> src = sourceDT->Snapshot();
+
+ if (group.mMaskSurface) {
+ Point finalOffset = group.mFinalTarget->GetDeviceOffset();
+ dt->SetTransform(group.mMaskTransform * Matrix::Translation(-finalOffset));
+ Matrix surfTransform = group.mMaskTransform;
+ surfTransform.Invert();
+ dt->MaskSurface(SurfacePattern(src, ExtendMode::CLAMP, surfTransform *
+ Matrix::Translation(group.mGroupOffset.x, group.mGroupOffset.y)),
+ group.mMaskSurface, Point(0, 0), DrawOptions(group.mOpacity, group.mOperator));
+ } else {
+ // For now this is required since our group offset is in device space of the final target,
+ // context but that may still have its own device offset. Once PushGroup/PopGroup logic is
+ // migrated to DrawTargets this can go as gfxContext::GetDeviceOffset will essentially
+ // always become null.
+ dt->SetTransform(Matrix::Translation(-group.mFinalTarget->GetDeviceOffset()));
+ dt->DrawSurface(src, Rect(group.mGroupOffset.x, group.mGroupOffset.y, src->GetSize().width, src->GetSize().height),
+ Rect(0, 0, src->GetSize().width, src->GetSize().height), DrawSurfaceOptions(SamplingFilter::POINT), DrawOptions(group.mOpacity, group.mOperator));
+ }
+
+ if (group.mNeedsClipToVisibleRegion) {
+ dt->PopClip();
+ }
+
+ group.mFinalTarget->Restore();
+}
+
+static IntRect
+ToInsideIntRect(const gfxRect& aRect)
+{
+ return IntRect::RoundIn(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
+}
+
+// A context helper for BasicLayerManager::PaintLayer() that holds all the
+// painting context together in a data structure so it can be easily passed
+// around. It also uses ensures that the Transform and Opaque rect are restored
+// to their former state on destruction.
+
+class PaintLayerContext {
+public:
+ PaintLayerContext(gfxContext* aTarget, Layer* aLayer,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData)
+ : mTarget(aTarget)
+ , mTargetMatrixSR(aTarget)
+ , mLayer(aLayer)
+ , mCallback(aCallback)
+ , mCallbackData(aCallbackData)
+ , mPushedOpaqueRect(false)
+ {}
+
+ ~PaintLayerContext()
+ {
+ // Matrix is restored by mTargetMatrixSR
+ if (mPushedOpaqueRect)
+ {
+ ClearOpaqueRect();
+ }
+ }
+
+ // Gets the effective transform and returns true if it is a 2D
+ // transform.
+ bool Setup2DTransform()
+ {
+ // Will return an identity matrix for 3d transforms.
+ return mLayer->GetEffectiveTransformForBuffer().CanDraw2D(&mTransform);
+ }
+
+ // Applies the effective transform if it's 2D. If it's a 3D transform then
+ // it applies an identity.
+ void Apply2DTransform()
+ {
+ mTarget->SetMatrix(ThebesMatrix(mTransform));
+ }
+
+ // Set the opaque rect to match the bounds of the visible region.
+ void AnnotateOpaqueRect()
+ {
+ const nsIntRegion visibleRegion = mLayer->GetLocalVisibleRegion().ToUnknownRegion();
+ const IntRect& bounds = visibleRegion.GetBounds();
+
+ DrawTarget *dt = mTarget->GetDrawTarget();
+ const IntRect& targetOpaqueRect = dt->GetOpaqueRect();
+
+ // Try to annotate currentSurface with a region of pixels that have been
+ // (or will be) painted opaque, if no such region is currently set.
+ if (targetOpaqueRect.IsEmpty() && visibleRegion.GetNumRects() == 1 &&
+ (mLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
+ !mTransform.HasNonAxisAlignedTransform()) {
+
+ gfx::Rect opaqueRect = dt->GetTransform().TransformBounds(
+ gfx::Rect(bounds.x, bounds.y, bounds.width, bounds.height));
+ opaqueRect.RoundIn();
+ IntRect intOpaqueRect;
+ if (opaqueRect.ToIntRect(&intOpaqueRect)) {
+ mTarget->GetDrawTarget()->SetOpaqueRect(intOpaqueRect);
+ mPushedOpaqueRect = true;
+ }
+ }
+ }
+
+ // Clear the Opaque rect. Although this doesn't really restore it to it's
+ // previous state it will happen on the exit path of the PaintLayer() so when
+ // painting is complete the opaque rect qill be clear.
+ void ClearOpaqueRect() {
+ mTarget->GetDrawTarget()->SetOpaqueRect(IntRect());
+ }
+
+ gfxContext* mTarget;
+ gfxContextMatrixAutoSaveRestore mTargetMatrixSR;
+ Layer* mLayer;
+ LayerManager::DrawPaintedLayerCallback mCallback;
+ void* mCallbackData;
+ Matrix mTransform;
+ bool mPushedOpaqueRect;
+};
+
+BasicLayerManager::BasicLayerManager(nsIWidget* aWidget)
+ : mPhase(PHASE_NONE)
+ , mWidget(aWidget)
+ , mDoubleBuffering(BufferMode::BUFFER_NONE)
+ , mType(BLM_WIDGET)
+ , mUsingDefaultTarget(false)
+ , mTransactionIncomplete(false)
+ , mCompositorMightResample(false)
+{
+ MOZ_COUNT_CTOR(BasicLayerManager);
+ NS_ASSERTION(aWidget, "Must provide a widget");
+}
+
+BasicLayerManager::BasicLayerManager(BasicLayerManagerType aType)
+ : mPhase(PHASE_NONE)
+ , mWidget(nullptr)
+ , mDoubleBuffering(BufferMode::BUFFER_NONE)
+ , mType(aType)
+ , mUsingDefaultTarget(false)
+ , mTransactionIncomplete(false)
+{
+ MOZ_COUNT_CTOR(BasicLayerManager);
+ MOZ_ASSERT(mType != BLM_WIDGET);
+}
+
+BasicLayerManager::~BasicLayerManager()
+{
+ NS_ASSERTION(!InTransaction(), "Died during transaction?");
+
+ ClearCachedResources();
+
+ mRoot = nullptr;
+
+ MOZ_COUNT_DTOR(BasicLayerManager);
+}
+
+void
+BasicLayerManager::SetDefaultTarget(gfxContext* aContext)
+{
+ NS_ASSERTION(!InTransaction(),
+ "Must set default target outside transaction");
+ mDefaultTarget = aContext;
+}
+
+void
+BasicLayerManager::SetDefaultTargetConfiguration(BufferMode aDoubleBuffering, ScreenRotation aRotation)
+{
+ mDoubleBuffering = aDoubleBuffering;
+}
+
+bool
+BasicLayerManager::BeginTransaction()
+{
+ mInTransaction = true;
+ mUsingDefaultTarget = true;
+ return BeginTransactionWithTarget(mDefaultTarget);
+}
+
+bool
+BasicLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
+{
+ mInTransaction = true;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ MOZ_LAYERS_LOG(("[----- BeginTransaction"));
+ Log();
+#endif
+
+ NS_ASSERTION(!InTransaction(), "Nested transactions not allowed");
+ mPhase = PHASE_CONSTRUCTION;
+ mTarget = aTarget;
+ return true;
+}
+
+static void
+TransformIntRect(IntRect& aRect, const Matrix& aMatrix,
+ IntRect (*aRoundMethod)(const gfxRect&))
+{
+ Rect gr = Rect(aRect.x, aRect.y, aRect.width, aRect.height);
+ gr = aMatrix.TransformBounds(gr);
+ aRect = (*aRoundMethod)(ThebesRect(gr));
+}
+
+/**
+ * This function assumes that GetEffectiveTransform transforms
+ * all layers to the same coordinate system (the "root coordinate system").
+ * It can't be used as is by accelerated layers because of intermediate surfaces.
+ * This must set the hidden flag to true or false on *all* layers in the subtree.
+ * It also sets the operator for all layers to "OVER", and call
+ * SetDrawAtomically(false).
+ * It clears mClipToVisibleRegion on all layers.
+ * @param aClipRect the cliprect, in the root coordinate system. We assume
+ * that any layer drawing is clipped to this rect. It is therefore not
+ * allowed to add to the opaque region outside that rect.
+ * @param aDirtyRect the dirty rect that will be painted, in the root
+ * coordinate system. Layers outside this rect should be hidden.
+ * @param aOpaqueRegion the opaque region covering aLayer, in the
+ * root coordinate system.
+ */
+enum {
+ ALLOW_OPAQUE = 0x01,
+};
+static void
+MarkLayersHidden(Layer* aLayer, const IntRect& aClipRect,
+ const IntRect& aDirtyRect,
+ nsIntRegion& aOpaqueRegion,
+ uint32_t aFlags)
+{
+ IntRect newClipRect(aClipRect);
+ uint32_t newFlags = aFlags;
+
+ // Allow aLayer or aLayer's descendants to cover underlying layers
+ // only if it's opaque.
+ if (aLayer->GetOpacity() != 1.0f) {
+ newFlags &= ~ALLOW_OPAQUE;
+ }
+
+ {
+ const Maybe<ParentLayerIntRect>& clipRect = aLayer->GetLocalClipRect();
+ if (clipRect) {
+ IntRect cr = clipRect->ToUnknownRect();
+ // clipRect is in the container's coordinate system. Get it into the
+ // global coordinate system.
+ if (aLayer->GetParent()) {
+ Matrix tr;
+ if (aLayer->GetParent()->GetEffectiveTransform().CanDraw2D(&tr)) {
+ // Clip rect is applied after aLayer's transform, i.e., in the coordinate
+ // system of aLayer's parent.
+ TransformIntRect(cr, tr, ToInsideIntRect);
+ } else {
+ cr.SetRect(0, 0, 0, 0);
+ }
+ }
+ newClipRect.IntersectRect(newClipRect, cr);
+ }
+ }
+
+ BasicImplData* data = ToData(aLayer);
+ data->SetOperator(CompositionOp::OP_OVER);
+ data->SetClipToVisibleRegion(false);
+ data->SetDrawAtomically(false);
+
+ if (!aLayer->AsContainerLayer()) {
+ Matrix transform;
+ if (!aLayer->GetEffectiveTransform().CanDraw2D(&transform)) {
+ data->SetHidden(false);
+ return;
+ }
+
+ nsIntRegion region = aLayer->GetLocalVisibleRegion().ToUnknownRegion();
+ IntRect r = region.GetBounds();
+ TransformIntRect(r, transform, ToOutsideIntRect);
+ r.IntersectRect(r, aDirtyRect);
+ data->SetHidden(aOpaqueRegion.Contains(r));
+
+ // Allow aLayer to cover underlying layers only if aLayer's
+ // content is opaque
+ if ((aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
+ (newFlags & ALLOW_OPAQUE)) {
+ for (auto iter = region.RectIter(); !iter.Done(); iter.Next()) {
+ r = iter.Get();
+ TransformIntRect(r, transform, ToInsideIntRect);
+
+ r.IntersectRect(r, newClipRect);
+ aOpaqueRegion.Or(aOpaqueRegion, r);
+ }
+ }
+ } else {
+ Layer* child = aLayer->GetLastChild();
+ bool allHidden = true;
+ for (; child; child = child->GetPrevSibling()) {
+ MarkLayersHidden(child, newClipRect, aDirtyRect, aOpaqueRegion, newFlags);
+ if (!ToData(child)->IsHidden()) {
+ allHidden = false;
+ }
+ }
+ data->SetHidden(allHidden);
+ }
+}
+
+/**
+ * This function assumes that GetEffectiveTransform transforms
+ * all layers to the same coordinate system (the "root coordinate system").
+ * MarkLayersHidden must be called before calling this.
+ * @param aVisibleRect the rectangle of aLayer that is visible (i.e. not
+ * clipped and in the dirty rect), in the root coordinate system.
+ */
+static void
+ApplyDoubleBuffering(Layer* aLayer, const IntRect& aVisibleRect)
+{
+ BasicImplData* data = ToData(aLayer);
+ if (data->IsHidden())
+ return;
+
+ IntRect newVisibleRect(aVisibleRect);
+
+ {
+ const Maybe<ParentLayerIntRect>& clipRect = aLayer->GetLocalClipRect();
+ if (clipRect) {
+ IntRect cr = clipRect->ToUnknownRect();
+ // clipRect is in the container's coordinate system. Get it into the
+ // global coordinate system.
+ if (aLayer->GetParent()) {
+ Matrix tr;
+ if (aLayer->GetParent()->GetEffectiveTransform().CanDraw2D(&tr)) {
+ NS_ASSERTION(!ThebesMatrix(tr).HasNonIntegerTranslation(),
+ "Parent can only have an integer translation");
+ cr += nsIntPoint(int32_t(tr._31), int32_t(tr._32));
+ } else {
+ NS_ERROR("Parent can only have an integer translation");
+ }
+ }
+ newVisibleRect.IntersectRect(newVisibleRect, cr);
+ }
+ }
+
+ BasicContainerLayer* container =
+ static_cast<BasicContainerLayer*>(aLayer->AsContainerLayer());
+ // Layers that act as their own backbuffers should be drawn to the destination
+ // using OP_SOURCE to ensure that alpha values in a transparent window are
+ // cleared. This can also be faster than OP_OVER.
+ if (!container) {
+ data->SetOperator(CompositionOp::OP_SOURCE);
+ data->SetDrawAtomically(true);
+ } else {
+ if (container->UseIntermediateSurface() ||
+ !container->ChildrenPartitionVisibleRegion(newVisibleRect)) {
+ // We need to double-buffer this container.
+ data->SetOperator(CompositionOp::OP_SOURCE);
+ container->ForceIntermediateSurface();
+ } else {
+ // Tell the children to clip to their visible regions so our assumption
+ // that they don't paint outside their visible regions is valid!
+ for (Layer* child = aLayer->GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ ToData(child)->SetClipToVisibleRegion(true);
+ ApplyDoubleBuffering(child, newVisibleRect);
+ }
+ }
+ }
+}
+
+void
+BasicLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags)
+{
+ mInTransaction = false;
+
+ EndTransactionInternal(aCallback, aCallbackData, aFlags);
+}
+
+void
+BasicLayerManager::AbortTransaction()
+{
+ NS_ASSERTION(InConstruction(), "Should be in construction phase");
+ mPhase = PHASE_NONE;
+ mUsingDefaultTarget = false;
+ mInTransaction = false;
+}
+
+bool
+BasicLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags)
+{
+ PROFILER_LABEL("BasicLayerManager", "EndTransactionInternal",
+ js::ProfileEntry::Category::GRAPHICS);
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ MOZ_LAYERS_LOG((" ----- (beginning paint)"));
+ Log();
+#endif
+
+ NS_ASSERTION(InConstruction(), "Should be in construction phase");
+ mPhase = PHASE_DRAWING;
+
+ RenderTraceLayers(mRoot, "FF00");
+
+ mTransactionIncomplete = false;
+
+ if (mRoot) {
+ if (aFlags & END_NO_COMPOSITE) {
+ // Apply pending tree updates before recomputing effective
+ // properties.
+ mRoot->ApplyPendingUpdatesToSubtree();
+ }
+
+ // Need to do this before we call ApplyDoubleBuffering,
+ // which depends on correct effective transforms
+ if (mTarget) {
+ mSnapEffectiveTransforms =
+ !mTarget->GetDrawTarget()->GetUserData(&sDisablePixelSnapping);
+ } else {
+ mSnapEffectiveTransforms = true;
+ }
+ mRoot->ComputeEffectiveTransforms(mTarget ? Matrix4x4::From2D(ToMatrix(mTarget->CurrentMatrix())) : Matrix4x4());
+
+ ToData(mRoot)->Validate(aCallback, aCallbackData, nullptr);
+ if (mRoot->GetMaskLayer()) {
+ ToData(mRoot->GetMaskLayer())->Validate(aCallback, aCallbackData, nullptr);
+ }
+ }
+
+ if (mTarget && mRoot &&
+ !(aFlags & END_NO_IMMEDIATE_REDRAW) &&
+ !(aFlags & END_NO_COMPOSITE)) {
+ IntRect clipRect;
+
+ {
+ gfxContextMatrixAutoSaveRestore save(mTarget);
+ mTarget->SetMatrix(gfxMatrix());
+ clipRect = ToOutsideIntRect(mTarget->GetClipExtents());
+ }
+
+ if (IsRetained()) {
+ nsIntRegion region;
+ MarkLayersHidden(mRoot, clipRect, clipRect, region, ALLOW_OPAQUE);
+ if (mUsingDefaultTarget && mDoubleBuffering != BufferMode::BUFFER_NONE) {
+ ApplyDoubleBuffering(mRoot, clipRect);
+ }
+ }
+
+ PaintLayer(mTarget, mRoot, aCallback, aCallbackData);
+ if (!mRegionToClear.IsEmpty()) {
+ for (auto iter = mRegionToClear.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& r = iter.Get();
+ mTarget->GetDrawTarget()->ClearRect(Rect(r.x, r.y, r.width, r.height));
+ }
+ }
+ if (mWidget) {
+ FlashWidgetUpdateArea(mTarget);
+ }
+ RecordFrame();
+ PostPresent();
+
+ if (!mTransactionIncomplete) {
+ // Clear out target if we have a complete transaction.
+ mTarget = nullptr;
+ }
+ }
+
+ if (mRoot) {
+ mAnimationReadyTime = TimeStamp::Now();
+ mRoot->StartPendingAnimations(mAnimationReadyTime);
+ }
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ Log();
+ MOZ_LAYERS_LOG(("]----- EndTransaction"));
+#endif
+
+ // Go back to the construction phase if the transaction isn't complete.
+ // Layout will update the layer tree and call EndTransaction().
+ mPhase = mTransactionIncomplete ? PHASE_CONSTRUCTION : PHASE_NONE;
+
+ if (!mTransactionIncomplete) {
+ // This is still valid if the transaction was incomplete.
+ mUsingDefaultTarget = false;
+ }
+
+ NS_ASSERTION(!aCallback || !mTransactionIncomplete,
+ "If callback is not null, transaction must be complete");
+
+ // XXX - We should probably assert here that for an incomplete transaction
+ // out target is the default target.
+
+ return !mTransactionIncomplete;
+}
+
+void
+BasicLayerManager::FlashWidgetUpdateArea(gfxContext *aContext)
+{
+ if (gfxPrefs::WidgetUpdateFlashing()) {
+ float r = float(rand()) / RAND_MAX;
+ float g = float(rand()) / RAND_MAX;
+ float b = float(rand()) / RAND_MAX;
+ aContext->SetColor(Color(r, g, b, 0.2f));
+ aContext->Paint();
+ }
+}
+
+bool
+BasicLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags)
+{
+ mInTransaction = false;
+
+ if (!mRoot) {
+ return false;
+ }
+
+ return EndTransactionInternal(nullptr, nullptr, aFlags);
+}
+
+void
+BasicLayerManager::SetRoot(Layer* aLayer)
+{
+ NS_ASSERTION(aLayer, "Root can't be null");
+ NS_ASSERTION(aLayer->Manager() == this, "Wrong manager");
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ mRoot = aLayer;
+}
+
+void
+BasicLayerManager::PaintSelfOrChildren(PaintLayerContext& aPaintContext,
+ gfxContext* aGroupTarget)
+{
+ MOZ_ASSERT(aGroupTarget);
+ BasicImplData* data = ToData(aPaintContext.mLayer);
+
+ /* Only paint ourself, or our children - This optimization relies on this! */
+ Layer* child = aPaintContext.mLayer->GetFirstChild();
+ if (!child) {
+ if (aPaintContext.mLayer->AsPaintedLayer()) {
+ data->PaintThebes(aGroupTarget, aPaintContext.mLayer->GetMaskLayer(),
+ aPaintContext.mCallback, aPaintContext.mCallbackData);
+ } else {
+ data->Paint(aGroupTarget->GetDrawTarget(),
+ aGroupTarget->GetDeviceOffset(),
+ aPaintContext.mLayer->GetMaskLayer());
+ }
+ } else {
+ ContainerLayer* container =
+ static_cast<ContainerLayer*>(aPaintContext.mLayer);
+ AutoTArray<Layer*, 12> children;
+ container->SortChildrenBy3DZOrder(children);
+ for (uint32_t i = 0; i < children.Length(); i++) {
+ Layer* layer = children.ElementAt(i);
+ if (layer->IsBackfaceHidden()) {
+ continue;
+ }
+ if (!layer->AsContainerLayer() && !layer->IsVisible()) {
+ continue;
+ }
+
+ PaintLayer(aGroupTarget, layer, aPaintContext.mCallback,
+ aPaintContext.mCallbackData);
+ if (mTransactionIncomplete)
+ break;
+ }
+ }
+}
+
+void
+BasicLayerManager::FlushGroup(PaintLayerContext& aPaintContext, bool aNeedsClipToVisibleRegion)
+{
+ // If we're doing our own double-buffering, we need to avoid drawing
+ // the results of an incomplete transaction to the destination surface ---
+ // that could cause flicker. Double-buffering is implemented using a
+ // temporary surface for one or more container layers, so we need to stop
+ // those temporary surfaces from being composited to aTarget.
+ // ApplyDoubleBuffering guarantees that this container layer can't
+ // intersect any other leaf layers, so if the transaction is not yet marked
+ // incomplete, the contents of this container layer are the final contents
+ // for the window.
+ if (!mTransactionIncomplete) {
+ if (aNeedsClipToVisibleRegion) {
+ gfxUtils::ClipToRegion(aPaintContext.mTarget,
+ aPaintContext.mLayer->GetLocalVisibleRegion().ToUnknownRegion());
+ }
+
+ CompositionOp op = GetEffectiveOperator(aPaintContext.mLayer);
+ AutoSetOperator setOperator(aPaintContext.mTarget, op);
+
+ PaintWithMask(aPaintContext.mTarget, aPaintContext.mLayer->GetEffectiveOpacity(),
+ aPaintContext.mLayer->GetMaskLayer());
+ }
+}
+
+/**
+ * Install the clip applied to the layer on the given gfxContext. The
+ * given gfxContext is the buffer that the layer will be painted to.
+ */
+static void
+InstallLayerClipPreserves3D(gfxContext* aTarget, Layer* aLayer)
+{
+ const Maybe<ParentLayerIntRect> &clipRect = aLayer->GetLocalClipRect();
+
+ if (!clipRect) {
+ return;
+ }
+ MOZ_ASSERT(!aLayer->Extend3DContext() ||
+ !aLayer->Combines3DTransformWithAncestors(),
+ "Layers in a preserve 3D context have no clip"
+ " except leaves and the estabisher!");
+
+ Layer* parent = aLayer->GetParent();
+ Matrix4x4 transform3d =
+ parent && parent->Extend3DContext() ?
+ parent->GetEffectiveTransform() :
+ Matrix4x4();
+ Matrix transform;
+ if (!transform3d.CanDraw2D(&transform)) {
+ gfxDevCrash(LogReason::CannotDraw3D) << "GFX: We should not have a 3D transform that CanDraw2D() is false!";
+ }
+ gfxMatrix oldTransform = aTarget->CurrentMatrix();
+ transform *= ToMatrix(oldTransform);
+ aTarget->SetMatrix(ThebesMatrix(transform));
+
+ aTarget->NewPath();
+ aTarget->SnappedRectangle(gfxRect(clipRect->x, clipRect->y,
+ clipRect->width, clipRect->height));
+ aTarget->Clip();
+
+ aTarget->SetMatrix(oldTransform);
+}
+
+void
+BasicLayerManager::PaintLayer(gfxContext* aTarget,
+ Layer* aLayer,
+ DrawPaintedLayerCallback aCallback,
+ void* aCallbackData)
+{
+ MOZ_ASSERT(aTarget);
+
+ PROFILER_LABEL("BasicLayerManager", "PaintLayer",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ PaintLayerContext paintLayerContext(aTarget, aLayer, aCallback, aCallbackData);
+
+ // Don't attempt to paint layers with a singular transform, cairo will
+ // just throw an error.
+ if (aLayer->GetEffectiveTransform().IsSingular()) {
+ return;
+ }
+
+ RenderTraceScope trace("BasicLayerManager::PaintLayer", "707070");
+
+ const Maybe<ParentLayerIntRect>& clipRect = aLayer->GetLocalClipRect();
+ BasicContainerLayer* container =
+ static_cast<BasicContainerLayer*>(aLayer->AsContainerLayer());
+ bool needsGroup = container && container->UseIntermediateSurface();
+ BasicImplData* data = ToData(aLayer);
+ bool needsClipToVisibleRegion =
+ data->GetClipToVisibleRegion() && !aLayer->AsPaintedLayer();
+ NS_ASSERTION(needsGroup || !container ||
+ container->GetOperator() == CompositionOp::OP_OVER,
+ "non-OVER operator should have forced UseIntermediateSurface");
+ NS_ASSERTION(!container || !aLayer->GetMaskLayer() ||
+ container->UseIntermediateSurface(),
+ "ContainerLayer with mask layer should force UseIntermediateSurface");
+
+ gfxContextAutoSaveRestore contextSR;
+ gfxMatrix transform;
+ // Will return an identity matrix for 3d transforms, and is handled separately below.
+ bool is2D = paintLayerContext.Setup2DTransform();
+ MOZ_ASSERT(is2D || needsGroup || !container ||
+ container->Extend3DContext() ||
+ container->Is3DContextLeaf(),
+ "Must PushGroup for 3d transforms!");
+
+ Layer* parent = aLayer->GetParent();
+ bool inPreserves3DChain = parent && parent->Extend3DContext();
+ bool needsSaveRestore =
+ needsGroup || clipRect || needsClipToVisibleRegion || !is2D ||
+ inPreserves3DChain;
+ if (needsSaveRestore) {
+ contextSR.SetContext(aTarget);
+
+ // The clips on ancestors on the preserved3d chain should be
+ // installed on the aTarget before painting the layer.
+ InstallLayerClipPreserves3D(aTarget, aLayer);
+ for (Layer* l = parent; l && l->Extend3DContext(); l = l->GetParent()) {
+ InstallLayerClipPreserves3D(aTarget, l);
+ }
+ }
+
+ paintLayerContext.Apply2DTransform();
+
+ nsIntRegion visibleRegion = aLayer->GetLocalVisibleRegion().ToUnknownRegion();
+ // If needsGroup is true, we'll clip to the visible region after we've popped the group
+ if (needsClipToVisibleRegion && !needsGroup) {
+ gfxUtils::ClipToRegion(aTarget, visibleRegion);
+ // Don't need to clip to visible region again
+ needsClipToVisibleRegion = false;
+ }
+
+ if (is2D) {
+ paintLayerContext.AnnotateOpaqueRect();
+ }
+
+ bool clipIsEmpty = aTarget->GetClipExtents().IsEmpty();
+ if (clipIsEmpty) {
+ PaintSelfOrChildren(paintLayerContext, aTarget);
+ return;
+ }
+
+ if (is2D) {
+ if (needsGroup) {
+ PushedGroup pushedGroup;
+ if (PushGroupForLayer(aTarget, aLayer, aLayer->GetLocalVisibleRegion().ToUnknownRegion(), pushedGroup)) {
+ PaintSelfOrChildren(paintLayerContext, pushedGroup.mGroupTarget);
+ PopGroupForLayer(pushedGroup);
+ }
+ } else {
+ PaintSelfOrChildren(paintLayerContext, aTarget);
+ }
+ } else {
+ if (!needsGroup && container) {
+ PaintSelfOrChildren(paintLayerContext, aTarget);
+ return;
+ }
+
+ IntRect bounds = visibleRegion.GetBounds();
+ // DrawTarget without the 3D transform applied:
+ RefPtr<DrawTarget> untransformedDT =
+ gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(bounds.width, bounds.height),
+ SurfaceFormat::B8G8R8A8);
+ if (!untransformedDT || !untransformedDT->IsValid()) {
+ return;
+ }
+ untransformedDT->SetTransform(Matrix::Translation(-Point(bounds.x, bounds.y)));
+
+ RefPtr<gfxContext> groupTarget =
+ gfxContext::CreatePreservingTransformOrNull(untransformedDT);
+ MOZ_ASSERT(groupTarget); // already checked the target above
+
+ PaintSelfOrChildren(paintLayerContext, groupTarget);
+
+ // Temporary fast fix for bug 725886
+ // Revert these changes when 725886 is ready
+#ifdef DEBUG
+ if (aLayer->GetDebugColorIndex() != 0) {
+ Color color((aLayer->GetDebugColorIndex() & 1) ? 1.f : 0.f,
+ (aLayer->GetDebugColorIndex() & 2) ? 1.f : 0.f,
+ (aLayer->GetDebugColorIndex() & 4) ? 1.f : 0.f);
+ untransformedDT->FillRect(Rect(bounds), ColorPattern(color));
+ }
+#endif
+ Matrix4x4 effectiveTransform = aLayer->GetEffectiveTransform();
+ Rect xformBounds =
+ effectiveTransform.TransformAndClipBounds(Rect(bounds),
+ ToRect(aTarget->GetClipExtents()));
+ xformBounds.RoundOut();
+ effectiveTransform.PostTranslate(-xformBounds.x, -xformBounds.y, 0);
+ effectiveTransform.PreTranslate(bounds.x, bounds.y, 0);
+
+ RefPtr<SourceSurface> untransformedSurf = untransformedDT->Snapshot();
+ RefPtr<DrawTarget> xformDT =
+ untransformedDT->CreateSimilarDrawTarget(IntSize::Truncate(xformBounds.width, xformBounds.height),
+ SurfaceFormat::B8G8R8A8);
+ RefPtr<SourceSurface> xformSurf;
+ if(xformDT && untransformedSurf &&
+ xformDT->Draw3DTransformedSurface(untransformedSurf, effectiveTransform)) {
+ xformSurf = xformDT->Snapshot();
+ }
+
+ if (xformSurf) {
+ aTarget->SetPattern(
+ new gfxPattern(xformSurf,
+ Matrix::Translation(xformBounds.TopLeft())));
+
+ // Azure doesn't support EXTEND_NONE, so to avoid extending the edges
+ // of the source surface out to the current clip region, clip to
+ // the rectangle of the result surface now.
+ aTarget->NewPath();
+ aTarget->SnappedRectangle(ThebesRect(xformBounds));
+ aTarget->Clip();
+ FlushGroup(paintLayerContext, needsClipToVisibleRegion);
+ }
+ }
+}
+
+void
+BasicLayerManager::ClearCachedResources(Layer* aSubtree)
+{
+ MOZ_ASSERT(!aSubtree || aSubtree->Manager() == this);
+ if (aSubtree) {
+ ClearLayer(aSubtree);
+ } else if (mRoot) {
+ ClearLayer(mRoot);
+ }
+}
+void
+BasicLayerManager::ClearLayer(Layer* aLayer)
+{
+ ToData(aLayer)->ClearCachedResources();
+ for (Layer* child = aLayer->GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ ClearLayer(child);
+ }
+}
+
+already_AddRefed<ReadbackLayer>
+BasicLayerManager::CreateReadbackLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ReadbackLayer> layer = new BasicReadbackLayer(this);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/basic/BasicLayers.h b/system/graphics/layers/basic/BasicLayers.h
new file mode 100644
index 000000000..beb4357bb
--- /dev/null
+++ b/system/graphics/layers/basic/BasicLayers.h
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#ifndef GFX_BASICLAYERS_H
+#define GFX_BASICLAYERS_H
+
+#include <stdint.h> // for INT32_MAX, int32_t
+#include "Layers.h" // for Layer (ptr only), etc
+#include "gfxTypes.h"
+#include "gfxContext.h" // for gfxContext
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/WidgetUtils.h" // for ScreenRotation
+#include "mozilla/layers/LayersTypes.h" // for BufferMode, LayersBackend, etc
+#include "nsAString.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for gfxContext::AddRef, etc
+#include "nsRegion.h" // for nsIntRegion
+#include "nscore.h" // for nsAString, etc
+
+class nsIWidget;
+
+namespace mozilla {
+namespace layers {
+
+class ImageFactory;
+class ImageLayer;
+class PaintLayerContext;
+class ReadbackLayer;
+
+/**
+ * This is a cairo/Thebes-only, main-thread-only implementation of layers.
+ *
+ * In each transaction, the client sets up the layer tree and then during
+ * the drawing phase, each PaintedLayer is painted directly into the target
+ * context (with appropriate clipping and Push/PopGroups performed
+ * between layers).
+ */
+class BasicLayerManager final :
+ public LayerManager
+{
+public:
+ enum BasicLayerManagerType {
+ BLM_WIDGET,
+ BLM_OFFSCREEN,
+ BLM_INACTIVE
+ };
+ /**
+ * Construct a BasicLayerManager which will have no default
+ * target context. SetDefaultTarget or BeginTransactionWithTarget
+ * must be called for any rendering to happen. PaintedLayers will not
+ * be retained.
+ */
+ explicit BasicLayerManager(BasicLayerManagerType aType);
+ /**
+ * Construct a BasicLayerManager which will have no default
+ * target context. SetDefaultTarget or BeginTransactionWithTarget
+ * must be called for any rendering to happen. PaintedLayers will be
+ * retained; that is, we will try to retain the visible contents of
+ * PaintedLayers as cairo surfaces. We create PaintedLayer buffers by
+ * creating similar surfaces to the default target context, or to
+ * aWidget's GetThebesSurface if there is no default target context, or
+ * to the passed-in context if there is no widget and no default
+ * target context.
+ *
+ * This does not keep a strong reference to the widget, so the caller
+ * must ensure that the widget outlives the layer manager or call
+ * ClearWidget before the widget dies.
+ */
+ explicit BasicLayerManager(nsIWidget* aWidget);
+
+protected:
+ virtual ~BasicLayerManager();
+
+public:
+ BasicLayerManager* AsBasicLayerManager() override { return this; }
+
+ /**
+ * Set the default target context that will be used when BeginTransaction
+ * is called. This can only be called outside a transaction.
+ *
+ * aDoubleBuffering can request double-buffering for drawing to the
+ * default target. When BUFFERED, the layer manager avoids blitting
+ * temporary results to aContext and then overpainting them with final
+ * results, by using a temporary buffer when necessary. In BUFFERED
+ * mode we always completely overwrite the contents of aContext's
+ * destination surface (within the clip region) using OP_SOURCE.
+ */
+ void SetDefaultTarget(gfxContext* aContext);
+ virtual void SetDefaultTargetConfiguration(BufferMode aDoubleBuffering, ScreenRotation aRotation);
+ gfxContext* GetDefaultTarget() { return mDefaultTarget; }
+
+ nsIWidget* GetRetainerWidget() { return mWidget; }
+ void ClearRetainerWidget() { mWidget = nullptr; }
+
+ virtual bool IsWidgetLayerManager() override { return mWidget != nullptr; }
+ virtual bool IsInactiveLayerManager() override { return mType == BLM_INACTIVE; }
+
+ virtual bool BeginTransaction() override;
+ virtual bool BeginTransactionWithTarget(gfxContext* aTarget) override;
+ virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) override;
+ virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags = END_DEFAULT) override;
+ virtual bool ShouldAvoidComponentAlphaLayers() override { return IsWidgetLayerManager(); }
+
+ void AbortTransaction();
+
+ virtual void SetRoot(Layer* aLayer) override;
+
+ virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() override;
+ virtual already_AddRefed<ContainerLayer> CreateContainerLayer() override;
+ virtual already_AddRefed<ImageLayer> CreateImageLayer() override;
+ virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() override;
+ virtual already_AddRefed<ColorLayer> CreateColorLayer() override;
+ virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer() override;
+ virtual ImageFactory *GetImageFactory();
+
+ virtual LayersBackend GetBackendType() override { return LayersBackend::LAYERS_BASIC; }
+ virtual void GetBackendName(nsAString& name) override { name.AssignLiteral("Basic"); }
+
+ bool InConstruction() { return mPhase == PHASE_CONSTRUCTION; }
+#ifdef DEBUG
+ bool InDrawing() { return mPhase == PHASE_DRAWING; }
+ bool InForward() { return mPhase == PHASE_FORWARD; }
+#endif
+ bool InTransaction() { return mPhase != PHASE_NONE; }
+
+ gfxContext* GetTarget() { return mTarget; }
+ void SetTarget(gfxContext* aTarget) { mUsingDefaultTarget = false; mTarget = aTarget; }
+ bool IsRetained() { return mWidget != nullptr; }
+
+ virtual const char* Name() const override { return "Basic"; }
+
+ // Clear the cached contents of this layer tree.
+ virtual void ClearCachedResources(Layer* aSubtree = nullptr) override;
+
+ void SetTransactionIncomplete() { mTransactionIncomplete = true; }
+ bool IsTransactionIncomplete() { return mTransactionIncomplete; }
+
+ struct PushedGroup
+ {
+ PushedGroup() : mFinalTarget(nullptr), mNeedsClipToVisibleRegion(false), mOperator(gfx::CompositionOp::OP_COUNT), mOpacity(0.0f){}
+ gfxContext* mFinalTarget;
+ RefPtr<gfxContext> mGroupTarget;
+ nsIntRegion mVisibleRegion;
+ bool mNeedsClipToVisibleRegion;
+ gfx::IntPoint mGroupOffset;
+ gfx::CompositionOp mOperator;
+ gfx::Float mOpacity;
+ RefPtr<gfx::SourceSurface> mMaskSurface;
+ gfx::Matrix mMaskTransform;
+ };
+
+ // Construct a PushedGroup for a specific layer.
+ // Return false if it has some errors in PushGroupForLayer(). Then, the
+ // "aGroupResult" is unavailable for future using.
+ bool PushGroupForLayer(gfxContext* aContext, Layer* aLayerContext, const nsIntRegion& aRegion, PushedGroup& aGroupResult);
+
+ void PopGroupForLayer(PushedGroup& aGroup);
+
+ virtual bool IsCompositingCheap() override { return false; }
+ virtual int32_t GetMaxTextureSize() const override { return INT32_MAX; }
+ bool CompositorMightResample() { return mCompositorMightResample; }
+
+protected:
+ enum TransactionPhase {
+ PHASE_NONE, PHASE_CONSTRUCTION, PHASE_DRAWING, PHASE_FORWARD
+ };
+ TransactionPhase mPhase;
+
+ // This is the main body of the PaintLayer routine which will if it has
+ // children, recurse into PaintLayer() otherwise it will paint using the
+ // underlying Paint() method of the Layer. It will not do both.
+ void PaintSelfOrChildren(PaintLayerContext& aPaintContext, gfxContext* aGroupTarget);
+
+ // Paint the group onto the underlying target. This is used by PaintLayer to
+ // flush the group to the underlying target.
+ void FlushGroup(PaintLayerContext& aPaintContext, bool aNeedsClipToVisibleRegion);
+
+ // Paints aLayer to mTarget.
+ void PaintLayer(gfxContext* aTarget,
+ Layer* aLayer,
+ DrawPaintedLayerCallback aCallback,
+ void* aCallbackData);
+
+ // Clear the contents of a layer
+ void ClearLayer(Layer* aLayer);
+
+ bool EndTransactionInternal(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags = END_DEFAULT);
+
+ void FlashWidgetUpdateArea(gfxContext* aContext);
+
+ // Widget whose surface should be used as the basis for PaintedLayer
+ // buffers.
+ nsIWidget* mWidget;
+ // The default context for BeginTransaction.
+ RefPtr<gfxContext> mDefaultTarget;
+ // The context to draw into.
+ RefPtr<gfxContext> mTarget;
+ // Image factory we use.
+ RefPtr<ImageFactory> mFactory;
+
+ BufferMode mDoubleBuffering;
+ BasicLayerManagerType mType;
+ bool mUsingDefaultTarget;
+ bool mTransactionIncomplete;
+ bool mCompositorMightResample;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_BASICLAYERS_H */
diff --git a/system/graphics/layers/basic/BasicLayersImpl.cpp b/system/graphics/layers/basic/BasicLayersImpl.cpp
new file mode 100644
index 000000000..c2262c512
--- /dev/null
+++ b/system/graphics/layers/basic/BasicLayersImpl.cpp
@@ -0,0 +1,211 @@
+/* -*- Mode: C++; tab-width: 2; 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 "BasicLayersImpl.h"
+#include <new> // for operator new
+#include "Layers.h" // for Layer, etc
+#include "basic/BasicImplData.h" // for BasicImplData
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/DebugOnly.h" // for DebugOnly
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "AutoMaskData.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+bool
+GetMaskData(Layer* aMaskLayer,
+ const Point& aDeviceOffset,
+ AutoMoz2DMaskData* aMaskData)
+{
+ if (aMaskLayer) {
+ RefPtr<SourceSurface> surface =
+ static_cast<BasicImplData*>(aMaskLayer->ImplData())->GetAsSourceSurface();
+ if (surface) {
+ Matrix transform;
+ Matrix4x4 effectiveTransform = aMaskLayer->GetEffectiveTransform();
+ DebugOnly<bool> maskIs2D = effectiveTransform.CanDraw2D(&transform);
+ NS_ASSERTION(maskIs2D, "How did we end up with a 3D transform here?!");
+ transform.PostTranslate(-aDeviceOffset.x, -aDeviceOffset.y);
+ aMaskData->Construct(transform, surface);
+ return true;
+ }
+ }
+ return false;
+}
+
+already_AddRefed<SourceSurface>
+GetMaskForLayer(Layer* aLayer, Matrix* aMaskTransform)
+{
+ if (!aLayer->GetMaskLayer()) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(aMaskTransform);
+
+ AutoMoz2DMaskData mask;
+ if (GetMaskData(aLayer->GetMaskLayer(), Point(), &mask)) {
+ *aMaskTransform = mask.GetTransform();
+ RefPtr<SourceSurface> surf = mask.GetSurface();
+ return surf.forget();
+ }
+
+ return nullptr;
+}
+
+void
+PaintWithMask(gfxContext* aContext, float aOpacity, Layer* aMaskLayer)
+{
+ AutoMoz2DMaskData mask;
+ if (GetMaskData(aMaskLayer, Point(), &mask)) {
+ aContext->SetMatrix(ThebesMatrix(mask.GetTransform()));
+ aContext->Mask(mask.GetSurface(), aOpacity);
+ return;
+ }
+
+ // if there is no mask, just paint normally
+ aContext->Paint(aOpacity);
+}
+
+void
+FillRectWithMask(DrawTarget* aDT,
+ const Rect& aRect,
+ const Color& aColor,
+ const DrawOptions& aOptions,
+ SourceSurface* aMaskSource,
+ const Matrix* aMaskTransform)
+{
+ if (aMaskSource && aMaskTransform) {
+ aDT->PushClipRect(aRect);
+ Matrix oldTransform = aDT->GetTransform();
+
+ aDT->SetTransform(*aMaskTransform);
+ aDT->MaskSurface(ColorPattern(aColor), aMaskSource, Point(), aOptions);
+ aDT->SetTransform(oldTransform);
+ aDT->PopClip();
+ return;
+ }
+
+ aDT->FillRect(aRect, ColorPattern(aColor), aOptions);
+}
+void
+FillRectWithMask(DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ const Rect& aRect,
+ const Color& aColor,
+ const DrawOptions& aOptions,
+ Layer* aMaskLayer)
+{
+ AutoMoz2DMaskData mask;
+ if (GetMaskData(aMaskLayer, aDeviceOffset, &mask)) {
+ const Matrix& maskTransform = mask.GetTransform();
+ FillRectWithMask(aDT, aRect, aColor, aOptions, mask.GetSurface(), &maskTransform);
+ return;
+ }
+
+ FillRectWithMask(aDT, aRect, aColor, aOptions);
+}
+
+void
+FillRectWithMask(DrawTarget* aDT,
+ const Rect& aRect,
+ SourceSurface* aSurface,
+ SamplingFilter aSamplingFilter,
+ const DrawOptions& aOptions,
+ ExtendMode aExtendMode,
+ SourceSurface* aMaskSource,
+ const Matrix* aMaskTransform,
+ const Matrix* aSurfaceTransform)
+{
+ if (aMaskSource && aMaskTransform) {
+ aDT->PushClipRect(aRect);
+ Matrix oldTransform = aDT->GetTransform();
+
+ Matrix inverseMask = *aMaskTransform;
+ inverseMask.Invert();
+
+ Matrix transform = oldTransform * inverseMask;
+ if (aSurfaceTransform) {
+ transform = (*aSurfaceTransform) * transform;
+ }
+
+ SurfacePattern source(aSurface, aExtendMode, transform, aSamplingFilter);
+
+ aDT->SetTransform(*aMaskTransform);
+ aDT->MaskSurface(source, aMaskSource, Point(0, 0), aOptions);
+ aDT->SetTransform(oldTransform);
+ aDT->PopClip();
+ return;
+ }
+
+ aDT->FillRect(aRect,
+ SurfacePattern(aSurface, aExtendMode,
+ aSurfaceTransform ? (*aSurfaceTransform) : Matrix(),
+ aSamplingFilter), aOptions);
+}
+
+void
+FillRectWithMask(DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ const Rect& aRect,
+ SourceSurface* aSurface,
+ SamplingFilter aSamplingFilter,
+ const DrawOptions& aOptions,
+ Layer* aMaskLayer)
+{
+ AutoMoz2DMaskData mask;
+ if (GetMaskData(aMaskLayer, aDeviceOffset, &mask)) {
+ const Matrix& maskTransform = mask.GetTransform();
+ FillRectWithMask(aDT, aRect, aSurface, aSamplingFilter, aOptions,
+ ExtendMode::CLAMP,
+ mask.GetSurface(), &maskTransform);
+ return;
+ }
+
+ FillRectWithMask(aDT, aRect, aSurface, aSamplingFilter, aOptions,
+ ExtendMode::CLAMP);
+}
+
+BasicImplData*
+ToData(Layer* aLayer)
+{
+ return static_cast<BasicImplData*>(aLayer->ImplData());
+}
+
+gfx::CompositionOp
+GetEffectiveOperator(Layer* aLayer)
+{
+ CompositionOp op = aLayer->GetEffectiveMixBlendMode();
+
+ if (op != CompositionOp::OP_OVER) {
+ return op;
+ }
+
+ return ToData(aLayer)->GetOperator();
+}
+
+ShadowableLayer*
+ToShadowable(Layer* aLayer)
+{
+ return aLayer->AsShadowableLayer();
+}
+
+bool
+ShouldShadow(Layer* aLayer)
+{
+ if (!ToShadowable(aLayer)) {
+ MOZ_ASSERT(aLayer->GetType() == Layer::TYPE_READBACK,
+ "Only expect not to shadow ReadbackLayers");
+ return false;
+ }
+ return true;
+}
+
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/basic/BasicLayersImpl.h b/system/graphics/layers/basic/BasicLayersImpl.h
new file mode 100644
index 000000000..5626fe329
--- /dev/null
+++ b/system/graphics/layers/basic/BasicLayersImpl.h
@@ -0,0 +1,151 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#ifndef GFX_BASICLAYERSIMPL_H
+#define GFX_BASICLAYERSIMPL_H
+
+#include "BasicImplData.h" // for BasicImplData
+#include "BasicLayers.h" // for BasicLayerManager
+#include "ReadbackLayer.h" // for ReadbackLayer
+#include "gfxContext.h" // for gfxContext, etc
+#include "mozilla/Attributes.h" // for MOZ_STACK_CLASS
+#include "mozilla/Maybe.h" // for Maybe
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for gfxContext::Release, etc
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace gfx {
+class DrawTarget;
+} // namespace gfx
+
+namespace layers {
+
+class AutoMoz2DMaskData;
+class Layer;
+
+class AutoSetOperator {
+ typedef mozilla::gfx::CompositionOp CompositionOp;
+public:
+ AutoSetOperator(gfxContext* aContext, CompositionOp aOperator) {
+ if (aOperator != CompositionOp::OP_OVER) {
+ aContext->SetOp(aOperator);
+ mContext = aContext;
+ }
+ }
+ ~AutoSetOperator() {
+ if (mContext) {
+ mContext->SetOp(CompositionOp::OP_OVER);
+ }
+ }
+private:
+ RefPtr<gfxContext> mContext;
+};
+
+class BasicReadbackLayer : public ReadbackLayer,
+ public BasicImplData
+{
+public:
+ explicit BasicReadbackLayer(BasicLayerManager* aLayerManager) :
+ ReadbackLayer(aLayerManager, static_cast<BasicImplData*>(this))
+ {
+ MOZ_COUNT_CTOR(BasicReadbackLayer);
+ }
+
+protected:
+ virtual ~BasicReadbackLayer()
+ {
+ MOZ_COUNT_DTOR(BasicReadbackLayer);
+ }
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion)
+ {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ReadbackLayer::SetVisibleRegion(aRegion);
+ }
+
+protected:
+ BasicLayerManager* BasicManager()
+ {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+};
+
+/*
+ * Extract a mask surface for a mask layer
+ * Returns true and through outparams a surface for the mask layer if
+ * a mask layer is present and has a valid surface and transform;
+ * false otherwise.
+ * The transform for the layer will be put in aMaskData
+ */
+bool
+GetMaskData(Layer* aMaskLayer,
+ const gfx::Point& aDeviceOffset,
+ AutoMoz2DMaskData* aMaskData);
+
+already_AddRefed<gfx::SourceSurface> GetMaskForLayer(Layer* aLayer, gfx::Matrix* aMaskTransform);
+
+// Paint the current source to a context using a mask, if present
+void
+PaintWithMask(gfxContext* aContext, float aOpacity, Layer* aMaskLayer);
+
+// Fill the rect with the source, using a mask and opacity, if present
+void
+FillRectWithMask(gfx::DrawTarget* aDT,
+ const gfx::Rect& aRect,
+ const gfx::Color& aColor,
+ const gfx::DrawOptions& aOptions,
+ gfx::SourceSurface* aMaskSource = nullptr,
+ const gfx::Matrix* aMaskTransform = nullptr);
+void
+FillRectWithMask(gfx::DrawTarget* aDT,
+ const gfx::Rect& aRect,
+ gfx::SourceSurface* aSurface,
+ gfx::SamplingFilter aSamplingFilter,
+ const gfx::DrawOptions& aOptions,
+ gfx::ExtendMode aExtendMode,
+ gfx::SourceSurface* aMaskSource = nullptr,
+ const gfx::Matrix* aMaskTransform = nullptr,
+ const gfx::Matrix* aSurfaceTransform = nullptr);
+void
+FillRectWithMask(gfx::DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ const gfx::Rect& aRect,
+ gfx::SourceSurface* aSurface,
+ gfx::SamplingFilter aSamplingFilter,
+ const gfx::DrawOptions& aOptions,
+ Layer* aMaskLayer);
+void
+FillRectWithMask(gfx::DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ const gfx::Rect& aRect,
+ const gfx::Color& aColor,
+ const gfx::DrawOptions& aOptions,
+ Layer* aMaskLayer);
+
+BasicImplData*
+ToData(Layer* aLayer);
+
+/**
+ * Returns the operator to be used when blending and compositing this layer.
+ * Currently there is no way to specify both a blending and a compositing
+ * operator other than normal and source over respectively.
+ *
+ * If the layer has
+ * an effective blend mode operator other than normal, as returned by
+ * GetEffectiveMixBlendMode, this operator is used for blending, and source
+ * over is used for compositing.
+ * If the blend mode for this layer is normal, the compositing operator
+ * returned by GetOperator is used.
+ */
+gfx::CompositionOp
+GetEffectiveOperator(Layer* aLayer);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/basic/BasicPaintedLayer.cpp b/system/graphics/layers/basic/BasicPaintedLayer.cpp
new file mode 100644
index 000000000..d2c456daa
--- /dev/null
+++ b/system/graphics/layers/basic/BasicPaintedLayer.cpp
@@ -0,0 +1,243 @@
+/* -*- Mode: C++; tab-width: 2; 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 "BasicPaintedLayer.h"
+#include <stdint.h> // for uint32_t
+#include "GeckoProfiler.h" // for PROFILER_LABEL
+#include "ReadbackLayer.h" // for ReadbackLayer, ReadbackSink
+#include "ReadbackProcessor.h" // for ReadbackProcessor::Update, etc
+#include "RenderTrace.h" // for RenderTraceInvalidateEnd, etc
+#include "BasicLayersImpl.h" // for AutoMaskData, etc
+#include "gfxContext.h" // for gfxContext, etc
+#include "gfxRect.h" // for gfxRect
+#include "gfxUtils.h" // for gfxUtils
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/gfx/Matrix.h" // for Matrix
+#include "mozilla/gfx/Rect.h" // for Rect, IntRect
+#include "mozilla/gfx/Types.h" // for Float, etc
+#include "mozilla/layers/LayersTypes.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for gfxContext::Release, etc
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl
+#include "AutoMaskData.h"
+#include "gfx2DGlue.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+static nsIntRegion
+IntersectWithClip(const nsIntRegion& aRegion, gfxContext* aContext)
+{
+ gfxRect clip = aContext->GetClipExtents();
+ nsIntRegion result;
+ result.And(aRegion, IntRect::RoundOut(clip.X(), clip.Y(),
+ clip.Width(), clip.Height()));
+ return result;
+}
+
+void
+BasicPaintedLayer::PaintThebes(gfxContext* aContext,
+ Layer* aMaskLayer,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData)
+{
+ PROFILER_LABEL("BasicPaintedLayer", "PaintThebes",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ NS_ASSERTION(BasicManager()->InDrawing(),
+ "Can only draw in drawing phase");
+
+ float opacity = GetEffectiveOpacity();
+ CompositionOp effectiveOperator = GetEffectiveOperator(this);
+
+ if (!BasicManager()->IsRetained()) {
+ mValidRegion.SetEmpty();
+ mContentClient->Clear();
+
+ nsIntRegion toDraw = IntersectWithClip(GetLocalVisibleRegion().ToUnknownRegion(), aContext);
+
+ RenderTraceInvalidateStart(this, "FFFF00", toDraw.GetBounds());
+
+ if (!toDraw.IsEmpty() && !IsHidden()) {
+ if (!aCallback) {
+ BasicManager()->SetTransactionIncomplete();
+ return;
+ }
+
+ aContext->Save();
+
+ bool needsGroup = opacity != 1.0 ||
+ effectiveOperator != CompositionOp::OP_OVER ||
+ aMaskLayer;
+ RefPtr<gfxContext> context = nullptr;
+ BasicLayerManager::PushedGroup group;
+ bool availableGroup = false;
+
+ if (needsGroup) {
+ availableGroup =
+ BasicManager()->PushGroupForLayer(aContext, this, toDraw, group);
+ if (availableGroup) {
+ context = group.mGroupTarget;
+ }
+ } else {
+ context = aContext;
+ }
+ if (context) {
+ SetAntialiasingFlags(this, context->GetDrawTarget());
+ aCallback(this, context, toDraw, toDraw, DrawRegionClip::NONE,
+ nsIntRegion(), aCallbackData);
+ }
+ if (needsGroup && availableGroup) {
+ BasicManager()->PopGroupForLayer(group);
+ }
+
+ aContext->Restore();
+ }
+
+ RenderTraceInvalidateEnd(this, "FFFF00");
+ return;
+ }
+
+ if (BasicManager()->IsTransactionIncomplete())
+ return;
+
+ gfxRect clipExtents;
+ clipExtents = aContext->GetClipExtents();
+
+ // Pull out the mask surface and transform here, because the mask
+ // is internal to basic layers
+ AutoMoz2DMaskData mask;
+ SourceSurface* maskSurface = nullptr;
+ Matrix maskTransform;
+ if (GetMaskData(aMaskLayer, aContext->GetDeviceOffset(), &mask)) {
+ maskSurface = mask.GetSurface();
+ maskTransform = mask.GetTransform();
+ }
+
+ if (!IsHidden() && !clipExtents.IsEmpty()) {
+ mContentClient->DrawTo(this, aContext->GetDrawTarget(), opacity,
+ effectiveOperator,
+ maskSurface, &maskTransform);
+ }
+}
+
+void
+BasicPaintedLayer::Validate(LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ ReadbackProcessor* aReadback)
+{
+ if (!mContentClient) {
+ // This client will have a null Forwarder, which means it will not have
+ // a ContentHost on the other side.
+ mContentClient = new ContentClientBasic(mBackend);
+ }
+
+ if (!BasicManager()->IsRetained()) {
+ return;
+ }
+
+ nsTArray<ReadbackProcessor::Update> readbackUpdates;
+ if (aReadback && UsedForReadback()) {
+ aReadback->GetPaintedLayerUpdates(this, &readbackUpdates);
+ }
+
+ uint32_t flags = 0;
+ if (BasicManager()->CompositorMightResample()) {
+ flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE;
+ }
+ if (!(flags & RotatedContentBuffer::PAINT_WILL_RESAMPLE)) {
+ if (MayResample()) {
+ flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE;
+ }
+ }
+ if (mDrawAtomically) {
+ flags |= RotatedContentBuffer::PAINT_NO_ROTATION;
+ }
+ PaintState state =
+ mContentClient->BeginPaintBuffer(this, flags);
+ mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate);
+
+ DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state);
+ if (target && target->IsValid()) {
+ // The area that became invalid and is visible needs to be repainted
+ // (this could be the whole visible area if our buffer switched
+ // from RGB to RGBA, because we might need to repaint with
+ // subpixel AA)
+ state.mRegionToInvalidate.And(state.mRegionToInvalidate,
+ GetLocalVisibleRegion().ToUnknownRegion());
+ SetAntialiasingFlags(this, target);
+
+ RenderTraceInvalidateStart(this, "FFFF00", state.mRegionToDraw.GetBounds());
+
+ RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(target);
+ MOZ_ASSERT(ctx); // already checked the target above
+
+ PaintBuffer(ctx,
+ state.mRegionToDraw, state.mRegionToDraw, state.mRegionToInvalidate,
+ state.mDidSelfCopy,
+ state.mClip,
+ aCallback, aCallbackData);
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PaintThebes", this));
+ Mutated();
+ ctx = nullptr;
+ mContentClient->ReturnDrawTargetToBuffer(target);
+ target = nullptr;
+
+ RenderTraceInvalidateEnd(this, "FFFF00");
+ } else {
+ if (target) {
+ mContentClient->ReturnDrawTargetToBuffer(target);
+ target = nullptr;
+ }
+
+ // It's possible that state.mRegionToInvalidate is nonempty here,
+ // if we are shrinking the valid region to nothing. So use mRegionToDraw
+ // instead.
+ NS_WARNING_ASSERTION(
+ state.mRegionToDraw.IsEmpty(),
+ "No context when we have something to draw, resource exhaustion?");
+ }
+
+ for (uint32_t i = 0; i < readbackUpdates.Length(); ++i) {
+ ReadbackProcessor::Update& update = readbackUpdates[i];
+ nsIntPoint offset = update.mLayer->GetBackgroundLayerOffset();
+ RefPtr<DrawTarget> dt =
+ update.mLayer->GetSink()->BeginUpdate(update.mUpdateRect + offset,
+ update.mSequenceCounter);
+ if (dt) {
+ NS_ASSERTION(GetEffectiveOpacity() == 1.0, "Should only read back opaque layers");
+ NS_ASSERTION(!GetMaskLayer(), "Should only read back layers without masks");
+ dt->SetTransform(dt->GetTransform().PreTranslate(offset.x, offset.y));
+ mContentClient->DrawTo(this, dt, 1.0, CompositionOp::OP_OVER,
+ nullptr, nullptr);
+ update.mLayer->GetSink()->EndUpdate(update.mUpdateRect + offset);
+ }
+ }
+}
+
+already_AddRefed<PaintedLayer>
+BasicLayerManager::CreatePaintedLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+
+ BackendType backend = gfxPlatform::GetPlatform()->GetDefaultContentBackend();
+
+ if (mDefaultTarget) {
+ backend = mDefaultTarget->GetDrawTarget()->GetBackendType();
+ } else if (mType == BLM_WIDGET) {
+ backend = gfxPlatform::GetPlatform()->GetContentBackendFor(LayersBackend::LAYERS_BASIC);
+ }
+
+ RefPtr<PaintedLayer> layer = new BasicPaintedLayer(this, backend);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/basic/BasicPaintedLayer.h b/system/graphics/layers/basic/BasicPaintedLayer.h
new file mode 100644
index 000000000..e616948e1
--- /dev/null
+++ b/system/graphics/layers/basic/BasicPaintedLayer.h
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#ifndef GFX_BASICPAINTEDLAYER_H
+#define GFX_BASICPAINTEDLAYER_H
+
+#include "Layers.h" // for PaintedLayer, LayerManager, etc
+#include "RotatedBuffer.h" // for RotatedContentBuffer, etc
+#include "BasicImplData.h" // for BasicImplData
+#include "BasicLayers.h" // for BasicLayerManager
+#include "gfxPoint.h" // for gfxPoint
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/layers/ContentClient.h" // for ContentClientBasic
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsRegion.h" // for nsIntRegion
+class gfxContext;
+
+namespace mozilla {
+namespace layers {
+
+class ReadbackProcessor;
+
+class BasicPaintedLayer : public PaintedLayer, public BasicImplData {
+public:
+ typedef RotatedContentBuffer::PaintState PaintState;
+ typedef RotatedContentBuffer::ContentType ContentType;
+
+ explicit BasicPaintedLayer(BasicLayerManager* aLayerManager, gfx::BackendType aBackend) :
+ PaintedLayer(aLayerManager, static_cast<BasicImplData*>(this)),
+ mContentClient(nullptr)
+ , mBackend(aBackend)
+ {
+ MOZ_COUNT_CTOR(BasicPaintedLayer);
+ }
+
+protected:
+ virtual ~BasicPaintedLayer()
+ {
+ MOZ_COUNT_DTOR(BasicPaintedLayer);
+ }
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ PaintedLayer::SetVisibleRegion(aRegion);
+ }
+ virtual void InvalidateRegion(const nsIntRegion& aRegion) override
+ {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ mInvalidRegion.Add(aRegion);
+ mValidRegion.Sub(mValidRegion, mInvalidRegion.GetRegion());
+ }
+
+ virtual void PaintThebes(gfxContext* aContext,
+ Layer* aMaskLayer,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData) override;
+
+ virtual void Validate(LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ ReadbackProcessor* aReadback) override;
+
+ virtual void ClearCachedResources() override
+ {
+ if (mContentClient) {
+ mContentClient->Clear();
+ }
+ mValidRegion.SetEmpty();
+ }
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+ {
+ if (!BasicManager()->IsRetained()) {
+ // Don't do any snapping of our transform, since we're just going to
+ // draw straight through without intermediate buffers.
+ mEffectiveTransform = GetLocalTransform() * aTransformToSurface;
+ if (gfxPoint(0,0) != mResidualTranslation) {
+ mResidualTranslation = gfxPoint(0,0);
+ mValidRegion.SetEmpty();
+ }
+ ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+ return;
+ }
+ PaintedLayer::ComputeEffectiveTransforms(aTransformToSurface);
+ }
+
+ BasicLayerManager* BasicManager()
+ {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+
+protected:
+ virtual void
+ PaintBuffer(gfxContext* aContext,
+ const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aExtendedRegionToDraw,
+ const nsIntRegion& aRegionToInvalidate,
+ bool aDidSelfCopy,
+ DrawRegionClip aClip,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData)
+ {
+ if (!aCallback) {
+ BasicManager()->SetTransactionIncomplete();
+ return;
+ }
+ aCallback(this, aContext, aExtendedRegionToDraw, aExtendedRegionToDraw,
+ aClip, aRegionToInvalidate, aCallbackData);
+ // Everything that's visible has been validated. Do this instead of just
+ // OR-ing with aRegionToDraw, since that can lead to a very complex region
+ // here (OR doesn't automatically simplify to the simplest possible
+ // representation of a region.)
+ nsIntRegion tmp;
+ tmp.Or(mVisibleRegion.ToUnknownRegion(), aExtendedRegionToDraw);
+ mValidRegion.Or(mValidRegion, tmp);
+ }
+
+ RefPtr<ContentClientBasic> mContentClient;
+ gfx::BackendType mBackend;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/basic/TextureClientX11.cpp b/system/graphics/layers/basic/TextureClientX11.cpp
new file mode 100644
index 000000000..c79ab1a53
--- /dev/null
+++ b/system/graphics/layers/basic/TextureClientX11.cpp
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 20; 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 "mozilla/layers/TextureClientX11.h"
+#include "mozilla/layers/CompositableClient.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/ShadowLayerUtilsX11.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Logging.h"
+#include "gfxXlibSurface.h"
+#include "gfx2DGlue.h"
+
+#include "mozilla/X11Util.h"
+#include <X11/Xlib.h>
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+X11TextureData::X11TextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ bool aClientDeallocation, bool aIsCrossProcess,
+ gfxXlibSurface* aSurface)
+: mSize(aSize)
+, mFormat(aFormat)
+, mSurface(aSurface)
+, mClientDeallocation(aClientDeallocation)
+, mIsCrossProcess(aIsCrossProcess)
+{
+ MOZ_ASSERT(mSurface);
+}
+
+bool
+X11TextureData::Lock(OpenMode aMode)
+{
+ return true;
+}
+
+void
+X11TextureData::Unlock()
+{
+ if (mSurface && mIsCrossProcess) {
+ FinishX(DefaultXDisplay());
+ }
+}
+
+void
+X11TextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = mSize;
+ aInfo.format = mFormat;
+ aInfo.supportsMoz2D = true;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = false;
+ aInfo.canExposeMappedData = false;
+}
+
+bool
+X11TextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ MOZ_ASSERT(mSurface);
+ if (!mSurface) {
+ return false;
+ }
+
+ if (!mClientDeallocation) {
+ // Pass to the host the responsibility of freeing the pixmap. ReleasePixmap means
+ // the underlying pixmap will not be deallocated in mSurface's destructor.
+ // ToSurfaceDescriptor is at most called once per TextureClient.
+ mSurface->ReleasePixmap();
+ }
+
+ aOutDescriptor = SurfaceDescriptorX11(mSurface);
+ return true;
+}
+
+already_AddRefed<gfx::DrawTarget>
+X11TextureData::BorrowDrawTarget()
+{
+ MOZ_ASSERT(mSurface);
+ if (!mSurface) {
+ return nullptr;
+ }
+
+ IntSize size = mSurface->GetSize();
+ RefPtr<gfx::DrawTarget> dt = Factory::CreateDrawTargetForCairoSurface(mSurface->CairoSurface(), size);
+
+ return dt.forget();
+}
+
+bool
+X11TextureData::UpdateFromSurface(gfx::SourceSurface* aSurface)
+{
+ RefPtr<DrawTarget> dt = BorrowDrawTarget();
+
+ if (!dt) {
+ return false;
+ }
+
+ dt->CopySurface(aSurface, IntRect(IntPoint(), aSurface->GetSize()), IntPoint());
+
+ return true;
+}
+
+void
+X11TextureData::Deallocate(LayersIPCChannel*)
+{
+ mSurface = nullptr;
+}
+
+TextureData*
+X11TextureData::CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags) const
+{
+ return X11TextureData::Create(mSize, mFormat, aFlags, aAllocator);
+}
+
+X11TextureData*
+X11TextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ TextureFlags aFlags, LayersIPCChannel* aAllocator)
+{
+ MOZ_ASSERT(aSize.width >= 0 && aSize.height >= 0);
+ if (aSize.width <= 0 || aSize.height <= 0 ||
+ aSize.width > XLIB_IMAGE_SIDE_SIZE_LIMIT ||
+ aSize.height > XLIB_IMAGE_SIDE_SIZE_LIMIT) {
+ gfxDebug() << "Asking for X11 surface of invalid size " << aSize.width << "x" << aSize.height;
+ return nullptr;
+ }
+ gfxImageFormat imageFormat = SurfaceFormatToImageFormat(aFormat);
+ RefPtr<gfxASurface> surface = gfxPlatform::GetPlatform()->CreateOffscreenSurface(aSize, imageFormat);
+ if (!surface || surface->GetType() != gfxSurfaceType::Xlib) {
+ NS_ERROR("creating Xlib surface failed!");
+ return nullptr;
+ }
+
+ gfxXlibSurface* xlibSurface = static_cast<gfxXlibSurface*>(surface.get());
+
+ bool crossProcess = !aAllocator->IsSameProcess();
+ X11TextureData* texture = new X11TextureData(aSize, aFormat,
+ !!(aFlags & TextureFlags::DEALLOCATE_CLIENT),
+ crossProcess,
+ xlibSurface);
+ if (crossProcess) {
+ FinishX(DefaultXDisplay());
+ }
+
+ return texture;
+}
+
+} // namespace
+} // namespace
diff --git a/system/graphics/layers/basic/TextureClientX11.h b/system/graphics/layers/basic/TextureClientX11.h
new file mode 100644
index 000000000..084538ea1
--- /dev/null
+++ b/system/graphics/layers/basic/TextureClientX11.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#ifndef MOZILLA_GFX_TEXTURECLIENT_X11_H
+#define MOZILLA_GFX_TEXTURECLIENT_X11_H
+
+#include "mozilla/layers/TextureClient.h"
+#include "ISurfaceAllocator.h" // For IsSurfaceDescriptorValid
+#include "mozilla/layers/ShadowLayerUtilsX11.h"
+
+namespace mozilla {
+namespace layers {
+
+class X11TextureData : public TextureData
+{
+public:
+ static X11TextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ TextureFlags aFlags, LayersIPCChannel* aAllocator);
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual bool Lock(OpenMode aMode) override;
+
+ virtual void Unlock() override;
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
+
+ virtual void Deallocate(LayersIPCChannel*) override;
+
+ virtual TextureData*
+ CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
+
+ virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
+
+protected:
+ X11TextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ bool aClientDeallocation, bool aIsCrossProcess,
+ gfxXlibSurface* aSurface);
+
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mFormat;
+ RefPtr<gfxXlibSurface> mSurface;
+ bool mClientDeallocation;
+ bool mIsCrossProcess;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/system/graphics/layers/basic/TextureHostBasic.cpp b/system/graphics/layers/basic/TextureHostBasic.cpp
new file mode 100644
index 000000000..1d671729e
--- /dev/null
+++ b/system/graphics/layers/basic/TextureHostBasic.cpp
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 20; 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 "TextureHostBasic.h"
+
+using namespace mozilla::gl;
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+already_AddRefed<TextureHost>
+CreateTextureHostBasic(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags)
+{
+ return CreateBackendIndependentTextureHost(aDesc, aDeallocator, aFlags);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/basic/TextureHostBasic.h b/system/graphics/layers/basic/TextureHostBasic.h
new file mode 100644
index 000000000..e08624f5a
--- /dev/null
+++ b/system/graphics/layers/basic/TextureHostBasic.h
@@ -0,0 +1,34 @@
+
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#ifndef MOZILLA_GFX_TEXTUREHOSTBASIC_H_
+#define MOZILLA_GFX_TEXTUREHOSTBASIC_H_
+
+#include "CompositableHost.h"
+#include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * A texture source interface that can be used by the software Compositor.
+ */
+class TextureSourceBasic
+{
+public:
+ TextureSourceBasic() : mFromYCBCR(false) {}
+ virtual ~TextureSourceBasic() {}
+ virtual gfx::SourceSurface* GetSurface(gfx::DrawTarget* aTarget) = 0;
+ virtual void SetBufferTextureHost(BufferTextureHost* aTexture) {}
+ bool mFromYCBCR; // we to track sources from YCBCR so we can use a less accurate fast path for video
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_TEXTUREHOSTBASIC_H_
diff --git a/system/graphics/layers/basic/X11BasicCompositor.cpp b/system/graphics/layers/basic/X11BasicCompositor.cpp
new file mode 100644
index 000000000..6f21d15d2
--- /dev/null
+++ b/system/graphics/layers/basic/X11BasicCompositor.cpp
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 20; 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 "X11BasicCompositor.h"
+#include "gfxPlatform.h"
+#include "gfx2DGlue.h"
+#include "gfxXlibSurface.h"
+#include "gfxImageSurface.h"
+#include "mozilla/X11Util.h"
+
+namespace mozilla {
+using namespace mozilla::gfx;
+
+namespace layers {
+
+bool
+X11DataTextureSourceBasic::Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion,
+ gfx::IntPoint* aSrcOffset)
+{
+ // Reallocate our internal X11 surface if we don't have a DrawTarget yet,
+ // or if we changed surface size or format since last update.
+ if (!mBufferDrawTarget ||
+ (aSurface->GetSize() != mBufferDrawTarget->GetSize()) ||
+ (aSurface->GetFormat() != mBufferDrawTarget->GetFormat())) {
+
+ RefPtr<gfxASurface> surf;
+ gfxImageFormat imageFormat = SurfaceFormatToImageFormat(aSurface->GetFormat());
+ Display *display = DefaultXDisplay();
+ Screen *screen = DefaultScreenOfDisplay(display);
+ XRenderPictFormat *xrenderFormat =
+ gfxXlibSurface::FindRenderFormat(display, imageFormat);
+
+ if (xrenderFormat) {
+ surf = gfxXlibSurface::Create(screen, xrenderFormat,
+ aSurface->GetSize());
+ }
+
+ if (!surf) {
+ NS_WARNING("Couldn't create native surface, fallback to image surface");
+ surf = new gfxImageSurface(aSurface->GetSize(), imageFormat);
+ }
+
+ mBufferDrawTarget = gfxPlatform::GetPlatform()->
+ CreateDrawTargetForSurface(surf, aSurface->GetSize());
+ }
+
+ // Image contents have changed, upload to our DrawTarget
+ // If aDestRegion is null, means we're updating the whole surface
+ // Note : Incremental update with a source offset is only used on Mac.
+ NS_ASSERTION(!aSrcOffset, "SrcOffset should not be used with linux OMTC basic");
+
+ if (aDestRegion) {
+ for (auto iter = aDestRegion->RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ IntRect srcRect(rect.x, rect.y, rect.width, rect.height);
+ IntPoint dstPoint(rect.x, rect.y);
+
+ // We're uploading regions to our buffer, so let's just copy contents over
+ mBufferDrawTarget->CopySurface(aSurface, srcRect, dstPoint);
+ }
+ } else {
+ // We're uploading the whole buffer, so let's just copy the full surface
+ IntSize size = aSurface->GetSize();
+ mBufferDrawTarget->CopySurface(aSurface, IntRect(0, 0, size.width, size.height),
+ IntPoint(0, 0));
+ }
+
+ return true;
+}
+
+TextureSourceBasic*
+X11DataTextureSourceBasic::AsSourceBasic()
+{
+ return this;
+}
+
+IntSize
+X11DataTextureSourceBasic::GetSize() const
+{
+ if (!mBufferDrawTarget) {
+ NS_WARNING("Trying to query the size of an uninitialized TextureSource");
+ return IntSize(0, 0);
+ } else {
+ return mBufferDrawTarget->GetSize();
+ }
+}
+
+gfx::SurfaceFormat
+X11DataTextureSourceBasic::GetFormat() const
+{
+ if (!mBufferDrawTarget) {
+ NS_WARNING("Trying to query the format of an uninitialized TextureSource");
+ return gfx::SurfaceFormat::UNKNOWN;
+ } else {
+ return mBufferDrawTarget->GetFormat();
+ }
+}
+
+SourceSurface*
+X11DataTextureSourceBasic::GetSurface(DrawTarget* aTarget)
+{
+ RefPtr<gfx::SourceSurface> surface;
+ if (mBufferDrawTarget) {
+ surface = mBufferDrawTarget->Snapshot();
+ return surface.get();
+ } else {
+ return nullptr;
+ }
+}
+
+void
+X11DataTextureSourceBasic::DeallocateDeviceData()
+{
+ mBufferDrawTarget = nullptr;
+}
+
+already_AddRefed<DataTextureSource>
+X11BasicCompositor::CreateDataTextureSource(TextureFlags aFlags)
+{
+ RefPtr<DataTextureSource> result =
+ new X11DataTextureSourceBasic();
+ return result.forget();
+}
+
+void
+X11BasicCompositor::EndFrame()
+{
+ BasicCompositor::EndFrame();
+ XFlush(DefaultXDisplay());
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/system/graphics/layers/basic/X11BasicCompositor.h b/system/graphics/layers/basic/X11BasicCompositor.h
new file mode 100644
index 000000000..2504e3f67
--- /dev/null
+++ b/system/graphics/layers/basic/X11BasicCompositor.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#ifndef MOZILLA_GFX_X11BASICCOMPOSITOR_H
+#define MOZILLA_GFX_X11BASICCOMPOSITOR_H
+
+#include "mozilla/layers/BasicCompositor.h"
+#include "mozilla/layers/X11TextureSourceBasic.h"
+#include "mozilla/layers/TextureHostBasic.h"
+#include "gfxXlibSurface.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+// TextureSource for Image-backed surfaces.
+class X11DataTextureSourceBasic : public DataTextureSource
+ , public TextureSourceBasic
+{
+public:
+ X11DataTextureSourceBasic() {};
+
+ virtual const char* Name() const override { return "X11DataTextureSourceBasic"; }
+
+ virtual bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr) override;
+
+ virtual TextureSourceBasic* AsSourceBasic() override;
+
+ virtual gfx::SourceSurface* GetSurface(gfx::DrawTarget* aTarget) override;
+
+ virtual void DeallocateDeviceData() override;
+
+ virtual gfx::IntSize GetSize() const override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+private:
+ // We are going to buffer layer content on this xlib draw target
+ RefPtr<mozilla::gfx::DrawTarget> mBufferDrawTarget;
+};
+
+class X11BasicCompositor : public BasicCompositor
+{
+public:
+ explicit X11BasicCompositor(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget)
+ : BasicCompositor(aParent, aWidget)
+ {}
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) override { return nullptr; }
+
+ virtual void EndFrame() override;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_X11BASICCOMPOSITOR_H */
diff --git a/system/graphics/layers/basic/X11TextureSourceBasic.cpp b/system/graphics/layers/basic/X11TextureSourceBasic.cpp
new file mode 100644
index 000000000..24f21e5fc
--- /dev/null
+++ b/system/graphics/layers/basic/X11TextureSourceBasic.cpp
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 20; 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 "X11TextureSourceBasic.h"
+#include "gfxXlibSurface.h"
+#include "gfx2DGlue.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+X11TextureSourceBasic::X11TextureSourceBasic(BasicCompositor* aCompositor, gfxXlibSurface* aSurface)
+ : mCompositor(aCompositor),
+ mSurface(aSurface)
+{
+}
+
+IntSize
+X11TextureSourceBasic::GetSize() const
+{
+ return mSurface->GetSize();
+}
+
+SurfaceFormat
+X11TextureSourceBasic::GetFormat() const
+{
+ gfxContentType type = mSurface->GetContentType();
+ return X11TextureSourceBasic::ContentTypeToSurfaceFormat(type);
+}
+
+SourceSurface*
+X11TextureSourceBasic::GetSurface(DrawTarget* aTarget)
+{
+ if (!mSourceSurface) {
+ mSourceSurface =
+ Factory::CreateSourceSurfaceForCairoSurface(mSurface->CairoSurface(),
+ GetSize(), GetFormat());
+ }
+ return mSourceSurface;
+}
+
+void
+X11TextureSourceBasic::SetCompositor(Compositor* aCompositor)
+{
+ mCompositor = AssertBasicCompositor(aCompositor);
+}
+
+SurfaceFormat
+X11TextureSourceBasic::ContentTypeToSurfaceFormat(gfxContentType aType)
+{
+ switch (aType) {
+ case gfxContentType::COLOR:
+ return SurfaceFormat::B8G8R8X8;
+ case gfxContentType::ALPHA:
+ return SurfaceFormat::A8;
+ case gfxContentType::COLOR_ALPHA:
+ return SurfaceFormat::B8G8R8A8;
+ default:
+ return SurfaceFormat::UNKNOWN;
+ }
+}
+
+}
+}
diff --git a/system/graphics/layers/basic/X11TextureSourceBasic.h b/system/graphics/layers/basic/X11TextureSourceBasic.h
new file mode 100644
index 000000000..f813560e0
--- /dev/null
+++ b/system/graphics/layers/basic/X11TextureSourceBasic.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#ifndef MOZILLA_GFX_X11TEXTURESOURCEBASIC__H
+#define MOZILLA_GFX_X11TEXTURESOURCEBASIC__H
+
+#include "mozilla/layers/BasicCompositor.h"
+#include "mozilla/layers/TextureHostBasic.h"
+#include "mozilla/layers/X11TextureHost.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+class BasicCompositor;
+
+// TextureSource for Xlib-backed surfaces.
+class X11TextureSourceBasic
+ : public TextureSourceBasic
+ , public X11TextureSource
+{
+public:
+ X11TextureSourceBasic(BasicCompositor* aCompositor, gfxXlibSurface* aSurface);
+
+ virtual const char* Name() const override { return "X11TextureSourceBasic"; }
+
+ virtual X11TextureSourceBasic* AsSourceBasic() override { return this; }
+
+ virtual gfx::IntSize GetSize() const override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual gfx::SourceSurface* GetSurface(gfx::DrawTarget* aTarget) override;
+
+ virtual void DeallocateDeviceData() override { }
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual void Updated() override { }
+
+ static gfx::SurfaceFormat ContentTypeToSurfaceFormat(gfxContentType aType);
+
+protected:
+ RefPtr<BasicCompositor> mCompositor;
+ RefPtr<gfxXlibSurface> mSurface;
+ RefPtr<gfx::SourceSurface> mSourceSurface;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_X11TEXTURESOURCEBASIC__H