michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "BasicCompositor.h" michael@0: #include "BasicLayersImpl.h" // for FillRectWithMask michael@0: #include "TextureHostBasic.h" michael@0: #include "mozilla/layers/Effects.h" michael@0: #include "mozilla/layers/YCbCrImageDataSerializer.h" michael@0: #include "nsIWidget.h" michael@0: #include "gfx2DGlue.h" michael@0: #include "mozilla/gfx/2D.h" michael@0: #include "mozilla/gfx/Helpers.h" michael@0: #include "gfxUtils.h" michael@0: #include "YCbCrUtils.h" michael@0: #include michael@0: #include "ImageContainer.h" michael@0: #include "gfxPrefs.h" michael@0: #define PIXMAN_DONT_DEFINE_STDINT michael@0: #include "pixman.h" // for pixman_f_transform, etc michael@0: michael@0: namespace mozilla { michael@0: using namespace mozilla::gfx; michael@0: michael@0: namespace layers { michael@0: michael@0: class DataTextureSourceBasic : public DataTextureSource michael@0: , public TextureSourceBasic michael@0: { michael@0: public: michael@0: michael@0: virtual TextureSourceBasic* AsSourceBasic() MOZ_OVERRIDE { return this; } michael@0: michael@0: virtual gfx::SourceSurface* GetSurface(DrawTarget* aTarget) MOZ_OVERRIDE { return mSurface; } michael@0: michael@0: SurfaceFormat GetFormat() const MOZ_OVERRIDE michael@0: { michael@0: return mSurface->GetFormat(); michael@0: } michael@0: michael@0: virtual IntSize GetSize() const MOZ_OVERRIDE michael@0: { michael@0: return mSurface->GetSize(); michael@0: } michael@0: michael@0: virtual bool Update(gfx::DataSourceSurface* aSurface, michael@0: nsIntRegion* aDestRegion = nullptr, michael@0: gfx::IntPoint* aSrcOffset = nullptr) MOZ_OVERRIDE michael@0: { michael@0: // XXX - For this to work with IncrementalContentHost we will need to support michael@0: // the aDestRegion and aSrcOffset parameters properly; michael@0: mSurface = aSurface; michael@0: return true; michael@0: } michael@0: michael@0: virtual void DeallocateDeviceData() MOZ_OVERRIDE michael@0: { michael@0: mSurface = nullptr; michael@0: SetUpdateSerial(0); michael@0: } michael@0: michael@0: public: michael@0: RefPtr mSurface; michael@0: }; michael@0: michael@0: BasicCompositor::BasicCompositor(nsIWidget *aWidget) michael@0: : mWidget(aWidget) michael@0: { michael@0: MOZ_COUNT_CTOR(BasicCompositor); michael@0: SetBackend(LayersBackend::LAYERS_BASIC); michael@0: } michael@0: michael@0: BasicCompositor::~BasicCompositor() michael@0: { michael@0: MOZ_COUNT_DTOR(BasicCompositor); michael@0: } michael@0: michael@0: void BasicCompositor::Destroy() michael@0: { michael@0: mWidget->CleanupRemoteDrawing(); michael@0: mWidget = nullptr; michael@0: } michael@0: michael@0: TemporaryRef michael@0: BasicCompositor::CreateRenderTarget(const IntRect& aRect, SurfaceInitMode aInit) michael@0: { michael@0: RefPtr target = mDrawTarget->CreateSimilarDrawTarget(aRect.Size(), SurfaceFormat::B8G8R8A8); michael@0: michael@0: RefPtr rt = new BasicCompositingRenderTarget(target, aRect); michael@0: michael@0: return rt.forget(); michael@0: } michael@0: michael@0: TemporaryRef michael@0: BasicCompositor::CreateRenderTargetFromSource(const IntRect &aRect, michael@0: const CompositingRenderTarget *aSource, michael@0: const IntPoint &aSourcePoint) michael@0: { michael@0: RefPtr target = mDrawTarget->CreateSimilarDrawTarget(aRect.Size(), SurfaceFormat::B8G8R8A8); michael@0: RefPtr rt = new BasicCompositingRenderTarget(target, aRect); michael@0: michael@0: DrawTarget *source; michael@0: if (aSource) { michael@0: const BasicCompositingRenderTarget* sourceSurface = michael@0: static_cast(aSource); michael@0: source = sourceSurface->mDrawTarget; michael@0: } else { michael@0: source = mDrawTarget; michael@0: } michael@0: michael@0: RefPtr snapshot = source->Snapshot(); michael@0: michael@0: IntRect sourceRect(aSourcePoint, aRect.Size()); michael@0: rt->mDrawTarget->CopySurface(snapshot, sourceRect, IntPoint(0, 0)); michael@0: return rt.forget(); michael@0: } michael@0: michael@0: TemporaryRef michael@0: BasicCompositor::CreateDataTextureSource(TextureFlags aFlags) michael@0: { michael@0: RefPtr result = new DataTextureSourceBasic(); michael@0: return result.forget(); michael@0: } michael@0: michael@0: bool michael@0: BasicCompositor::SupportsEffect(EffectTypes aEffect) michael@0: { michael@0: return static_cast(aEffect) != EFFECT_YCBCR; michael@0: } michael@0: michael@0: static void michael@0: DrawSurfaceWithTextureCoords(DrawTarget *aDest, michael@0: const gfx::Rect& aDestRect, michael@0: SourceSurface *aSource, michael@0: const gfx::Rect& aTextureCoords, michael@0: gfx::Filter aFilter, michael@0: float aOpacity, michael@0: SourceSurface *aMask, michael@0: const Matrix* aMaskTransform) michael@0: { michael@0: // Convert aTextureCoords into aSource's coordinate space michael@0: gfxRect sourceRect(aTextureCoords.x * aSource->GetSize().width, michael@0: aTextureCoords.y * aSource->GetSize().height, michael@0: aTextureCoords.width * aSource->GetSize().width, michael@0: aTextureCoords.height * aSource->GetSize().height); michael@0: // Compute a transform that maps sourceRect to aDestRect. michael@0: gfxMatrix transform = michael@0: gfxUtils::TransformRectToRect(sourceRect, michael@0: gfxPoint(aDestRect.x, aDestRect.y), michael@0: gfxPoint(aDestRect.XMost(), aDestRect.y), michael@0: gfxPoint(aDestRect.XMost(), aDestRect.YMost())); michael@0: Matrix matrix = ToMatrix(transform); michael@0: michael@0: // Only use REPEAT if aTextureCoords is outside (0, 0, 1, 1). michael@0: gfx::Rect unitRect(0, 0, 1, 1); michael@0: ExtendMode mode = unitRect.Contains(aTextureCoords) ? ExtendMode::CLAMP : ExtendMode::REPEAT; michael@0: michael@0: FillRectWithMask(aDest, aDestRect, aSource, aFilter, DrawOptions(aOpacity), michael@0: mode, aMask, aMaskTransform, &matrix); michael@0: } michael@0: michael@0: static pixman_transform michael@0: Matrix3DToPixman(const gfx3DMatrix& aMatrix) michael@0: { michael@0: pixman_f_transform transform; michael@0: michael@0: transform.m[0][0] = aMatrix._11; michael@0: transform.m[0][1] = aMatrix._21; michael@0: transform.m[0][2] = aMatrix._41; michael@0: transform.m[1][0] = aMatrix._12; michael@0: transform.m[1][1] = aMatrix._22; michael@0: transform.m[1][2] = aMatrix._42; michael@0: transform.m[2][0] = aMatrix._14; michael@0: transform.m[2][1] = aMatrix._24; michael@0: transform.m[2][2] = aMatrix._44; michael@0: michael@0: pixman_transform result; michael@0: pixman_transform_from_pixman_f_transform(&result, &transform); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: static void michael@0: PixmanTransform(DataSourceSurface* aDest, michael@0: DataSourceSurface* aSource, michael@0: const gfx3DMatrix& aTransform, michael@0: gfxPoint aDestOffset) michael@0: { michael@0: IntSize destSize = aDest->GetSize(); michael@0: pixman_image_t* dest = pixman_image_create_bits(PIXMAN_a8r8g8b8, michael@0: destSize.width, michael@0: destSize.height, michael@0: (uint32_t*)aDest->GetData(), michael@0: aDest->Stride()); michael@0: michael@0: IntSize srcSize = aSource->GetSize(); michael@0: pixman_image_t* src = pixman_image_create_bits(PIXMAN_a8r8g8b8, michael@0: srcSize.width, michael@0: srcSize.height, michael@0: (uint32_t*)aSource->GetData(), michael@0: aSource->Stride()); michael@0: michael@0: NS_ABORT_IF_FALSE(src && dest, "Failed to create pixman images?"); michael@0: michael@0: pixman_transform pixTransform = Matrix3DToPixman(aTransform); michael@0: pixman_transform pixTransformInverted; michael@0: michael@0: // If the transform is singular then nothing would be drawn anyway, return here michael@0: if (!pixman_transform_invert(&pixTransformInverted, &pixTransform)) { michael@0: pixman_image_unref(dest); michael@0: pixman_image_unref(src); michael@0: return; michael@0: } michael@0: pixman_image_set_transform(src, &pixTransformInverted); michael@0: michael@0: pixman_image_composite32(PIXMAN_OP_SRC, michael@0: src, michael@0: nullptr, michael@0: dest, michael@0: aDestOffset.x, michael@0: aDestOffset.y, michael@0: 0, michael@0: 0, michael@0: 0, michael@0: 0, michael@0: destSize.width, michael@0: destSize.height); michael@0: michael@0: pixman_image_unref(dest); michael@0: pixman_image_unref(src); michael@0: } michael@0: michael@0: static inline IntRect michael@0: RoundOut(Rect r) michael@0: { michael@0: r.RoundOut(); michael@0: return IntRect(r.x, r.y, r.width, r.height); michael@0: } michael@0: michael@0: void michael@0: BasicCompositor::DrawQuad(const gfx::Rect& aRect, michael@0: const gfx::Rect& aClipRect, michael@0: const EffectChain &aEffectChain, michael@0: gfx::Float aOpacity, michael@0: const gfx::Matrix4x4 &aTransform) michael@0: { michael@0: RefPtr buffer = mRenderTarget->mDrawTarget; michael@0: michael@0: // For 2D drawing, |dest| and |buffer| are the same surface. For 3D drawing, michael@0: // |dest| is a temporary surface. michael@0: RefPtr dest = buffer; michael@0: michael@0: buffer->PushClipRect(aClipRect); michael@0: AutoSaveTransform autoSaveTransform(dest); michael@0: michael@0: Matrix newTransform; michael@0: Rect transformBounds; michael@0: gfx3DMatrix new3DTransform; michael@0: IntPoint offset = mRenderTarget->GetOrigin(); michael@0: michael@0: if (aTransform.Is2D()) { michael@0: newTransform = aTransform.As2D(); michael@0: } else { michael@0: // Create a temporary surface for the transform. michael@0: dest = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(RoundOut(aRect).Size(), SurfaceFormat::B8G8R8A8); michael@0: if (!dest) { michael@0: return; michael@0: } michael@0: michael@0: // Get the bounds post-transform. michael@0: To3DMatrix(aTransform, new3DTransform); michael@0: gfxRect bounds = new3DTransform.TransformBounds(ThebesRect(aRect)); michael@0: bounds.IntersectRect(bounds, gfxRect(offset.x, offset.y, buffer->GetSize().width, buffer->GetSize().height)); michael@0: michael@0: transformBounds = ToRect(bounds); michael@0: transformBounds.RoundOut(); michael@0: michael@0: // Propagate the coordinate offset to our 2D draw target. michael@0: newTransform.Translate(transformBounds.x, transformBounds.y); michael@0: michael@0: // When we apply the 3D transformation, we do it against a temporary michael@0: // surface, so undo the coordinate offset. michael@0: new3DTransform = new3DTransform * gfx3DMatrix::Translation(-transformBounds.x, -transformBounds.y, 0); michael@0: michael@0: transformBounds.MoveTo(0, 0); michael@0: } michael@0: michael@0: newTransform.PostTranslate(-offset.x, -offset.y); michael@0: buffer->SetTransform(newTransform); michael@0: michael@0: RefPtr sourceMask; michael@0: Matrix maskTransform; michael@0: if (aEffectChain.mSecondaryEffects[EFFECT_MASK]) { michael@0: EffectMask *effectMask = static_cast(aEffectChain.mSecondaryEffects[EFFECT_MASK].get()); michael@0: sourceMask = effectMask->mMaskTexture->AsSourceBasic()->GetSurface(dest); michael@0: MOZ_ASSERT(effectMask->mMaskTransform.Is2D(), "How did we end up with a 3D transform here?!"); michael@0: MOZ_ASSERT(!effectMask->mIs3D); michael@0: maskTransform = effectMask->mMaskTransform.As2D(); michael@0: maskTransform.Translate(-offset.x, -offset.y); michael@0: } michael@0: michael@0: switch (aEffectChain.mPrimaryEffect->mType) { michael@0: case EFFECT_SOLID_COLOR: { michael@0: EffectSolidColor* effectSolidColor = michael@0: static_cast(aEffectChain.mPrimaryEffect.get()); michael@0: michael@0: FillRectWithMask(dest, aRect, effectSolidColor->mColor, michael@0: DrawOptions(aOpacity), sourceMask, &maskTransform); michael@0: break; michael@0: } michael@0: case EFFECT_RGB: { michael@0: TexturedEffect* texturedEffect = michael@0: static_cast(aEffectChain.mPrimaryEffect.get()); michael@0: TextureSourceBasic* source = texturedEffect->mTexture->AsSourceBasic(); michael@0: michael@0: DrawSurfaceWithTextureCoords(dest, aRect, michael@0: source->GetSurface(dest), michael@0: texturedEffect->mTextureCoords, michael@0: texturedEffect->mFilter, michael@0: aOpacity, sourceMask, &maskTransform); michael@0: break; michael@0: } michael@0: case EFFECT_YCBCR: { michael@0: NS_RUNTIMEABORT("Can't (easily) support component alpha with BasicCompositor!"); michael@0: break; michael@0: } michael@0: case EFFECT_RENDER_TARGET: { michael@0: EffectRenderTarget* effectRenderTarget = michael@0: static_cast(aEffectChain.mPrimaryEffect.get()); michael@0: RefPtr surface michael@0: = static_cast(effectRenderTarget->mRenderTarget.get()); michael@0: RefPtr sourceSurf = surface->mDrawTarget->Snapshot(); michael@0: michael@0: DrawSurfaceWithTextureCoords(dest, aRect, michael@0: sourceSurf, michael@0: effectRenderTarget->mTextureCoords, michael@0: effectRenderTarget->mFilter, michael@0: aOpacity, sourceMask, &maskTransform); michael@0: break; michael@0: } michael@0: case EFFECT_COMPONENT_ALPHA: { michael@0: NS_RUNTIMEABORT("Can't (easily) support component alpha with BasicCompositor!"); michael@0: break; michael@0: } michael@0: default: { michael@0: NS_RUNTIMEABORT("Invalid effect type!"); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (!aTransform.Is2D()) { michael@0: dest->Flush(); michael@0: michael@0: RefPtr snapshot = dest->Snapshot(); michael@0: RefPtr source = snapshot->GetDataSurface(); michael@0: RefPtr temp = michael@0: Factory::CreateDataSourceSurface(RoundOut(transformBounds).Size(), SurfaceFormat::B8G8R8A8); michael@0: if (!temp) { michael@0: return; michael@0: } michael@0: michael@0: PixmanTransform(temp, source, new3DTransform, gfxPoint(0, 0)); michael@0: michael@0: buffer->DrawSurface(temp, transformBounds, transformBounds); michael@0: } michael@0: michael@0: buffer->PopClip(); michael@0: } michael@0: michael@0: void michael@0: BasicCompositor::BeginFrame(const nsIntRegion& aInvalidRegion, michael@0: const gfx::Rect *aClipRectIn, michael@0: const gfx::Matrix& aTransform, michael@0: const gfx::Rect& aRenderBounds, michael@0: gfx::Rect *aClipRectOut /* = nullptr */, michael@0: gfx::Rect *aRenderBoundsOut /* = nullptr */) michael@0: { michael@0: nsIntRect intRect; michael@0: mWidget->GetClientBounds(intRect); michael@0: mWidgetSize = gfx::ToIntSize(intRect.Size()); michael@0: michael@0: // The result of GetClientBounds is shifted over by the size of the window michael@0: // manager styling. We want to ignore that. michael@0: intRect.MoveTo(0, 0); michael@0: Rect rect = Rect(0, 0, intRect.width, intRect.height); michael@0: michael@0: // Sometimes the invalid region is larger than we want to draw. michael@0: nsIntRegion invalidRegionSafe; michael@0: invalidRegionSafe.And(aInvalidRegion, intRect); michael@0: michael@0: // FIXME: Redraw the whole screen in every frame to work around bug 972728. michael@0: invalidRegionSafe = intRect; michael@0: michael@0: nsIntRect invalidRect = invalidRegionSafe.GetBounds(); michael@0: mInvalidRect = IntRect(invalidRect.x, invalidRect.y, invalidRect.width, invalidRect.height); michael@0: mInvalidRegion = invalidRegionSafe; michael@0: michael@0: if (aRenderBoundsOut) { michael@0: *aRenderBoundsOut = Rect(); michael@0: } michael@0: michael@0: if (mInvalidRect.width <= 0 || mInvalidRect.height <= 0) { michael@0: return; michael@0: } michael@0: michael@0: if (mCopyTarget) { michael@0: // If we have a copy target, then we don't have a widget-provided mDrawTarget (currently). Create a dummy michael@0: // placeholder so that CreateRenderTarget() works. michael@0: mDrawTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(IntSize(1,1), SurfaceFormat::B8G8R8A8); michael@0: } else { michael@0: mDrawTarget = mWidget->StartRemoteDrawing(); michael@0: } michael@0: if (!mDrawTarget) { michael@0: return; michael@0: } michael@0: michael@0: // Setup an intermediate render target to buffer all compositing. We will michael@0: // copy this into mDrawTarget (the widget), and/or mCopyTarget in EndFrame() michael@0: RefPtr target = CreateRenderTarget(mInvalidRect, INIT_MODE_CLEAR); michael@0: SetRenderTarget(target); michael@0: michael@0: // We only allocate a surface sized to the invalidated region, so we need to michael@0: // translate future coordinates. michael@0: Matrix transform; michael@0: transform.Translate(-invalidRect.x, -invalidRect.y); michael@0: mRenderTarget->mDrawTarget->SetTransform(transform); michael@0: michael@0: gfxUtils::ClipToRegion(mRenderTarget->mDrawTarget, invalidRegionSafe); michael@0: michael@0: if (aRenderBoundsOut) { michael@0: *aRenderBoundsOut = rect; michael@0: } michael@0: michael@0: if (aClipRectIn) { michael@0: mRenderTarget->mDrawTarget->PushClipRect(*aClipRectIn); michael@0: } else { michael@0: mRenderTarget->mDrawTarget->PushClipRect(rect); michael@0: if (aClipRectOut) { michael@0: *aClipRectOut = rect; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: BasicCompositor::EndFrame() michael@0: { michael@0: // Pop aClipRectIn/bounds rect michael@0: mRenderTarget->mDrawTarget->PopClip(); michael@0: michael@0: if (gfxPrefs::WidgetUpdateFlashing()) { michael@0: float r = float(rand()) / RAND_MAX; michael@0: float g = float(rand()) / RAND_MAX; michael@0: float b = float(rand()) / RAND_MAX; michael@0: // We're still clipped to mInvalidRegion, so just fill the bounds. michael@0: mRenderTarget->mDrawTarget->FillRect(ToRect(mInvalidRegion.GetBounds()), michael@0: ColorPattern(Color(r, g, b, 0.2f))); michael@0: } michael@0: michael@0: // Pop aInvalidregion michael@0: mRenderTarget->mDrawTarget->PopClip(); michael@0: michael@0: // Note: Most platforms require us to buffer drawing to the widget surface. michael@0: // That's why we don't draw to mDrawTarget directly. michael@0: RefPtr source = mRenderTarget->mDrawTarget->Snapshot(); michael@0: RefPtr dest(mCopyTarget ? mCopyTarget : mDrawTarget); michael@0: michael@0: // The source DrawTarget is clipped to the invalidation region, so we have michael@0: // to copy the individual rectangles in the region or else we'll draw blank michael@0: // pixels. michael@0: nsIntRegionRectIterator iter(mInvalidRegion); michael@0: for (const nsIntRect *r = iter.Next(); r; r = iter.Next()) { michael@0: dest->CopySurface(source, michael@0: IntRect(r->x - mInvalidRect.x, r->y - mInvalidRect.y, r->width, r->height), michael@0: IntPoint(r->x, r->y)); michael@0: } michael@0: if (!mCopyTarget) { michael@0: mWidget->EndRemoteDrawing(); michael@0: } michael@0: michael@0: mDrawTarget = nullptr; michael@0: mRenderTarget = nullptr; michael@0: } michael@0: michael@0: void michael@0: BasicCompositor::AbortFrame() michael@0: { michael@0: mRenderTarget->mDrawTarget->PopClip(); michael@0: mRenderTarget->mDrawTarget->PopClip(); michael@0: mDrawTarget = nullptr; michael@0: mRenderTarget = nullptr; michael@0: } michael@0: michael@0: } michael@0: }