/* -*- 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/ContentHost.h" #include "mozilla/layers/Effects.h" #include "nsPrintfCString.h" namespace mozilla { using namespace gfx; namespace layers { ContentHostBase::ContentHostBase(const TextureInfo& aTextureInfo) : ContentHost(aTextureInfo) , mPaintWillResample(false) , mInitialised(false) {} ContentHostBase::~ContentHostBase() {} TextureHost* ContentHostBase::GetTextureHost() { return mTextureHost; } void ContentHostBase::DestroyFrontHost() { MOZ_ASSERT(!mTextureHost || mTextureHost->GetDeAllocator(), "We won't be able to destroy our SurfaceDescriptor"); mTextureHost = nullptr; mTextureHostOnWhite = nullptr; } void ContentHostBase::Composite(EffectChain& aEffectChain, float aOpacity, const gfx::Matrix4x4& aTransform, const Point& aOffset, const Filter& aFilter, const Rect& aClipRect, const nsIntRegion* aVisibleRegion, TiledLayerProperties* aLayerProperties) { NS_ASSERTION(aVisibleRegion, "Requires a visible region"); AutoLockTextureHost lock(mTextureHost); AutoLockTextureHost lockOnWhite(mTextureHostOnWhite); if (!mTextureHost || !lock.IsValid() || !lockOnWhite.IsValid()) { return; } RefPtr effect = CreateTexturedEffect(mTextureHost, mTextureHostOnWhite, aFilter); aEffectChain.mPrimaryEffect = effect; nsIntRegion tmpRegion; const nsIntRegion* renderRegion; if (PaintWillResample()) { // If we're resampling, then the texture image will contain exactly the // entire visible region's bounds, and we should draw it all in one quad // to avoid unexpected aliasing. tmpRegion = aVisibleRegion->GetBounds(); renderRegion = &tmpRegion; } else { renderRegion = aVisibleRegion; } nsIntRegion region(*renderRegion); nsIntPoint origin = GetOriginOffset(); region.MoveBy(-origin); // translate into TexImage space, buffer origin might not be at texture (0,0) // Figure out the intersecting draw region TextureSource* source = mTextureHost; MOZ_ASSERT(source); gfx::IntSize texSize = source->GetSize(); nsIntRect textureRect = nsIntRect(0, 0, texSize.width, texSize.height); textureRect.MoveBy(region.GetBounds().TopLeft()); nsIntRegion subregion; subregion.And(region, textureRect); if (subregion.IsEmpty()) { // Region is empty, nothing to draw return; } nsIntRegion screenRects; nsIntRegion regionRects; // Collect texture/screen coordinates for drawing nsIntRegionRectIterator iter(subregion); while (const nsIntRect* iterRect = iter.Next()) { nsIntRect regionRect = *iterRect; nsIntRect screenRect = regionRect; screenRect.MoveBy(origin); screenRects.Or(screenRects, screenRect); regionRects.Or(regionRects, regionRect); } TileIterator* tileIter = source->AsTileIterator(); TileIterator* iterOnWhite = nullptr; if (tileIter) { tileIter->BeginTileIteration(); } if (mTextureHostOnWhite) { iterOnWhite = mTextureHostOnWhite->AsTileIterator(); MOZ_ASSERT(!tileIter || tileIter->GetTileCount() == iterOnWhite->GetTileCount(), "Tile count mismatch on component alpha texture"); if (iterOnWhite) { iterOnWhite->BeginTileIteration(); } } bool usingTiles = (tileIter && tileIter->GetTileCount() > 1); do { if (iterOnWhite) { MOZ_ASSERT(iterOnWhite->GetTileRect() == tileIter->GetTileRect(), "component alpha textures should be the same size."); } nsIntRect texRect = tileIter ? tileIter->GetTileRect() : nsIntRect(0, 0, texSize.width, texSize.height); // Draw texture. If we're using tiles, we do repeating manually, as texture // repeat would cause each individual tile to repeat instead of the // compound texture as a whole. This involves drawing at most 4 sections, // 2 for each axis that has texture repeat. for (int y = 0; y < (usingTiles ? 2 : 1); y++) { for (int x = 0; x < (usingTiles ? 2 : 1); x++) { nsIntRect currentTileRect(texRect); currentTileRect.MoveBy(x * texSize.width, y * texSize.height); nsIntRegionRectIterator screenIter(screenRects); nsIntRegionRectIterator regionIter(regionRects); const nsIntRect* screenRect; const nsIntRect* regionRect; while ((screenRect = screenIter.Next()) && (regionRect = regionIter.Next())) { nsIntRect tileScreenRect(*screenRect); nsIntRect tileRegionRect(*regionRect); // When we're using tiles, find the intersection between the tile // rect and this region rect. Tiling is then handled by the // outer for-loops and modifying the tile rect. if (usingTiles) { tileScreenRect.MoveBy(-origin); tileScreenRect = tileScreenRect.Intersect(currentTileRect); tileScreenRect.MoveBy(origin); if (tileScreenRect.IsEmpty()) continue; tileRegionRect = regionRect->Intersect(currentTileRect); tileRegionRect.MoveBy(-currentTileRect.TopLeft()); } gfx::Rect rect(tileScreenRect.x, tileScreenRect.y, tileScreenRect.width, tileScreenRect.height); effect->mTextureCoords = Rect(Float(tileRegionRect.x) / texRect.width, Float(tileRegionRect.y) / texRect.height, Float(tileRegionRect.width) / texRect.width, Float(tileRegionRect.height) / texRect.height); GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform, aOffset); } } } if (iterOnWhite) { iterOnWhite->NextTile(); } } while (usingTiles && tileIter->NextTile()); if (tileIter) { tileIter->EndTileIteration(); } if (iterOnWhite) { iterOnWhite->EndTileIteration(); } } void ContentHostBase::SetCompositor(Compositor* aCompositor) { CompositableHost::SetCompositor(aCompositor); if (mTextureHost) { mTextureHost->SetCompositor(aCompositor); } if (mTextureHostOnWhite) { mTextureHostOnWhite->SetCompositor(aCompositor); } } ContentHostSingleBuffered::~ContentHostSingleBuffered() { DestroyTextures(); DestroyFrontHost(); } bool ContentHostSingleBuffered::EnsureTextureHost(TextureIdentifier aTextureId, const SurfaceDescriptor& aSurface, ISurfaceAllocator* aAllocator, const TextureInfo& aTextureInfo) { MOZ_ASSERT(aTextureId == TextureFront); mNewFrontHost = TextureHost::CreateTextureHost(aSurface.type(), aTextureInfo.mTextureHostFlags, aTextureInfo.mTextureFlags); mNewFrontHost->SetBuffer(new SurfaceDescriptor(aSurface), aAllocator); Compositor* compositor = GetCompositor(); if (compositor) { mNewFrontHost->SetCompositor(compositor); } return true; } void ContentHostSingleBuffered::DestroyTextures() { MOZ_ASSERT(!mNewFrontHost || mNewFrontHost->GetDeAllocator(), "We won't be able to destroy our SurfaceDescriptor"); mNewFrontHost = nullptr; // don't touch mTextureHost, we might need it for compositing } void ContentHostSingleBuffered::UpdateThebes(const ThebesBufferData& aData, const nsIntRegion& aUpdated, const nsIntRegion& aOldValidRegionBack, nsIntRegion* aUpdatedRegionBack) { aUpdatedRegionBack->SetEmpty(); if (!mTextureHost && !mNewFrontHost) { mInitialised = false; return; } if (mNewFrontHost) { DestroyFrontHost(); mTextureHost = mNewFrontHost; mNewFrontHost = nullptr; } MOZ_ASSERT(mTextureHost); // updated is in screen coordinates. Convert it to buffer coordinates. nsIntRegion destRegion(aUpdated); destRegion.MoveBy(-aData.rect().TopLeft()); // Correct for rotation destRegion.MoveBy(aData.rotation()); gfxIntSize size = aData.rect().Size(); nsIntRect destBounds = destRegion.GetBounds(); destRegion.MoveBy((destBounds.x >= size.width) ? -size.width : 0, (destBounds.y >= size.height) ? -size.height : 0); // There's code to make sure that updated regions don't cross rotation // boundaries, so assert here that this is the case MOZ_ASSERT((destBounds.x % size.width) + destBounds.width <= size.width, "updated region lies across rotation boundaries!"); MOZ_ASSERT((destBounds.y % size.height) + destBounds.height <= size.height, "updated region lies across rotation boundaries!"); mTextureHost->Update(*mTextureHost->GetBuffer(), &destRegion); mInitialised = true; mBufferRect = aData.rect(); mBufferRotation = aData.rotation(); } ContentHostDoubleBuffered::~ContentHostDoubleBuffered() { DestroyTextures(); DestroyFrontHost(); } bool ContentHostDoubleBuffered::EnsureTextureHost(TextureIdentifier aTextureId, const SurfaceDescriptor& aSurface, ISurfaceAllocator* aAllocator, const TextureInfo& aTextureInfo) { RefPtr newHost = TextureHost::CreateTextureHost(aSurface.type(), aTextureInfo.mTextureHostFlags, aTextureInfo.mTextureFlags); newHost->SetBuffer(new SurfaceDescriptor(aSurface), aAllocator); Compositor* compositor = GetCompositor(); if (compositor) { newHost->SetCompositor(compositor); } if (aTextureId == TextureFront) { mNewFrontHost = newHost; return true; } if (aTextureId == TextureBack) { mBackHost = newHost; mBufferRect = nsIntRect(); mBufferRotation = nsIntPoint(); return true; } NS_ERROR("Bad texture identifier"); return false; } void ContentHostDoubleBuffered::DestroyTextures() { if (mNewFrontHost) { MOZ_ASSERT(mNewFrontHost->GetDeAllocator(), "We won't be able to destroy our SurfaceDescriptor"); mNewFrontHost = nullptr; } if (mBackHost) { MOZ_ASSERT(mBackHost->GetDeAllocator(), "We won't be able to destroy our SurfaceDescriptor"); mBackHost = nullptr; } // don't touch mTextureHost, we might need it for compositing } void ContentHostDoubleBuffered::UpdateThebes(const ThebesBufferData& aData, const nsIntRegion& aUpdated, const nsIntRegion& aOldValidRegionBack, nsIntRegion* aUpdatedRegionBack) { if (!mTextureHost && !mNewFrontHost) { mInitialised = false; *aUpdatedRegionBack = aUpdated; return; } if (mNewFrontHost) { DestroyFrontHost(); mTextureHost = mNewFrontHost; mNewFrontHost = nullptr; } MOZ_ASSERT(mTextureHost); MOZ_ASSERT(mBackHost); RefPtr oldFront = mTextureHost; mTextureHost = mBackHost; mBackHost = oldFront; mTextureHost->Update(*mTextureHost->GetBuffer()); mInitialised = true; mBufferRect = aData.rect(); mBufferRotation = aData.rotation(); *aUpdatedRegionBack = aUpdated; // Save the current valid region of our front buffer, because if // we're double buffering, it's going to be the valid region for the // next back buffer sent back to the renderer. // // NB: we rely here on the fact that mValidRegion is initialized to // empty, and that the first time Swap() is called we don't have a // valid front buffer that we're going to return to content. mValidRegionForNextBackBuffer = aOldValidRegionBack; } #ifdef MOZ_LAYERS_HAVE_LOG void ContentHostSingleBuffered::PrintInfo(nsACString& aTo, const char* aPrefix) { aTo += aPrefix; aTo += nsPrintfCString("ContentHostSingleBuffered (0x%p)", this); AppendToString(aTo, mBufferRect, " [buffer-rect=", "]"); AppendToString(aTo, mBufferRotation, " [buffer-rotation=", "]"); if (PaintWillResample()) { aTo += " [paint-will-resample]"; } nsAutoCString pfx(aPrefix); pfx += " "; if (mTextureHost) { aTo += "\n"; mTextureHost->PrintInfo(aTo, pfx.get()); } } void ContentHostDoubleBuffered::PrintInfo(nsACString& aTo, const char* aPrefix) { aTo += aPrefix; aTo += nsPrintfCString("ContentHostDoubleBuffered (0x%p)", this); AppendToString(aTo, mBufferRect, " [buffer-rect=", "]"); AppendToString(aTo, mBufferRotation, " [buffer-rotation=", "]"); if (PaintWillResample()) { aTo += " [paint-will-resample]"; } nsAutoCString prefix(aPrefix); prefix += " "; if (mTextureHost) { aTo += "\n"; mTextureHost->PrintInfo(aTo, prefix.get()); } if (mBackHost) { aTo += "\n"; mBackHost->PrintInfo(aTo, prefix.get()); } } #endif } // namespace } // namespace