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 "mozilla/layers/ContentHost.h" michael@0: #include "LayersLogging.h" // for AppendToString michael@0: #include "gfx2DGlue.h" // for ContentForFormat michael@0: #include "mozilla/gfx/Point.h" // for IntSize michael@0: #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc michael@0: #include "mozilla/gfx/BaseRect.h" // for BaseRect michael@0: #include "mozilla/layers/Compositor.h" // for Compositor michael@0: #include "mozilla/layers/Effects.h" // for TexturedEffect, Effect, etc michael@0: #include "mozilla/layers/LayersMessages.h" // for ThebesBufferData michael@0: #include "nsAString.h" michael@0: #include "nsPrintfCString.h" // for nsPrintfCString michael@0: #include "nsString.h" // for nsAutoCString michael@0: #include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL michael@0: michael@0: namespace mozilla { michael@0: namespace gfx { michael@0: class Matrix4x4; michael@0: } michael@0: using namespace gfx; michael@0: michael@0: namespace layers { michael@0: michael@0: ContentHostBase::ContentHostBase(const TextureInfo& aTextureInfo) michael@0: : ContentHost(aTextureInfo) michael@0: , mPaintWillResample(false) michael@0: , mInitialised(false) michael@0: {} michael@0: michael@0: ContentHostBase::~ContentHostBase() michael@0: { michael@0: } michael@0: michael@0: struct AutoLockContentHost michael@0: { michael@0: AutoLockContentHost(ContentHostBase* aHost) michael@0: : mHost(aHost) michael@0: { michael@0: mSucceeded = mHost->Lock(); michael@0: } michael@0: michael@0: ~AutoLockContentHost() michael@0: { michael@0: if (mSucceeded) { michael@0: mHost->Unlock(); michael@0: } michael@0: } michael@0: michael@0: bool Failed() { return !mSucceeded; } michael@0: michael@0: ContentHostBase* mHost; michael@0: bool mSucceeded; michael@0: }; michael@0: michael@0: void michael@0: ContentHostBase::Composite(EffectChain& aEffectChain, michael@0: float aOpacity, michael@0: const gfx::Matrix4x4& aTransform, michael@0: const Filter& aFilter, michael@0: const Rect& aClipRect, michael@0: const nsIntRegion* aVisibleRegion, michael@0: TiledLayerProperties* aLayerProperties) michael@0: { michael@0: NS_ASSERTION(aVisibleRegion, "Requires a visible region"); michael@0: michael@0: AutoLockContentHost lock(this); michael@0: if (lock.Failed()) { michael@0: return; michael@0: } michael@0: michael@0: RefPtr source = GetTextureSource(); michael@0: RefPtr sourceOnWhite = GetTextureSourceOnWhite(); michael@0: michael@0: if (!source) { michael@0: return; michael@0: } michael@0: RefPtr effect = michael@0: CreateTexturedEffect(source, sourceOnWhite, aFilter); michael@0: michael@0: if (!effect) { michael@0: return; michael@0: } michael@0: michael@0: aEffectChain.mPrimaryEffect = effect; michael@0: michael@0: nsIntRegion tmpRegion; michael@0: const nsIntRegion* renderRegion; michael@0: if (PaintWillResample()) { michael@0: // If we're resampling, then the texture image will contain exactly the michael@0: // entire visible region's bounds, and we should draw it all in one quad michael@0: // to avoid unexpected aliasing. michael@0: tmpRegion = aVisibleRegion->GetBounds(); michael@0: renderRegion = &tmpRegion; michael@0: } else { michael@0: renderRegion = aVisibleRegion; michael@0: } michael@0: michael@0: nsIntRegion region(*renderRegion); michael@0: nsIntPoint origin = GetOriginOffset(); michael@0: // translate into TexImage space, buffer origin might not be at texture (0,0) michael@0: region.MoveBy(-origin); michael@0: michael@0: // Figure out the intersecting draw region michael@0: gfx::IntSize texSize = source->GetSize(); michael@0: nsIntRect textureRect = nsIntRect(0, 0, texSize.width, texSize.height); michael@0: textureRect.MoveBy(region.GetBounds().TopLeft()); michael@0: nsIntRegion subregion; michael@0: subregion.And(region, textureRect); michael@0: if (subregion.IsEmpty()) { michael@0: // Region is empty, nothing to draw michael@0: return; michael@0: } michael@0: michael@0: nsIntRegion screenRects; michael@0: nsIntRegion regionRects; michael@0: michael@0: // Collect texture/screen coordinates for drawing michael@0: nsIntRegionRectIterator iter(subregion); michael@0: while (const nsIntRect* iterRect = iter.Next()) { michael@0: nsIntRect regionRect = *iterRect; michael@0: nsIntRect screenRect = regionRect; michael@0: screenRect.MoveBy(origin); michael@0: michael@0: screenRects.Or(screenRects, screenRect); michael@0: regionRects.Or(regionRects, regionRect); michael@0: } michael@0: michael@0: TileIterator* tileIter = source->AsTileIterator(); michael@0: TileIterator* iterOnWhite = nullptr; michael@0: if (tileIter) { michael@0: tileIter->BeginTileIteration(); michael@0: } michael@0: michael@0: if (sourceOnWhite) { michael@0: iterOnWhite = sourceOnWhite->AsTileIterator(); michael@0: MOZ_ASSERT(!tileIter || tileIter->GetTileCount() == iterOnWhite->GetTileCount(), michael@0: "Tile count mismatch on component alpha texture"); michael@0: if (iterOnWhite) { michael@0: iterOnWhite->BeginTileIteration(); michael@0: } michael@0: } michael@0: michael@0: bool usingTiles = (tileIter && tileIter->GetTileCount() > 1); michael@0: do { michael@0: if (iterOnWhite) { michael@0: MOZ_ASSERT(iterOnWhite->GetTileRect() == tileIter->GetTileRect(), michael@0: "component alpha textures should be the same size."); michael@0: } michael@0: michael@0: nsIntRect texRect = tileIter ? tileIter->GetTileRect() michael@0: : nsIntRect(0, 0, michael@0: texSize.width, michael@0: texSize.height); michael@0: michael@0: // Draw texture. If we're using tiles, we do repeating manually, as texture michael@0: // repeat would cause each individual tile to repeat instead of the michael@0: // compound texture as a whole. This involves drawing at most 4 sections, michael@0: // 2 for each axis that has texture repeat. michael@0: for (int y = 0; y < (usingTiles ? 2 : 1); y++) { michael@0: for (int x = 0; x < (usingTiles ? 2 : 1); x++) { michael@0: nsIntRect currentTileRect(texRect); michael@0: currentTileRect.MoveBy(x * texSize.width, y * texSize.height); michael@0: michael@0: nsIntRegionRectIterator screenIter(screenRects); michael@0: nsIntRegionRectIterator regionIter(regionRects); michael@0: michael@0: const nsIntRect* screenRect; michael@0: const nsIntRect* regionRect; michael@0: while ((screenRect = screenIter.Next()) && michael@0: (regionRect = regionIter.Next())) { michael@0: nsIntRect tileScreenRect(*screenRect); michael@0: nsIntRect tileRegionRect(*regionRect); michael@0: michael@0: // When we're using tiles, find the intersection between the tile michael@0: // rect and this region rect. Tiling is then handled by the michael@0: // outer for-loops and modifying the tile rect. michael@0: if (usingTiles) { michael@0: tileScreenRect.MoveBy(-origin); michael@0: tileScreenRect = tileScreenRect.Intersect(currentTileRect); michael@0: tileScreenRect.MoveBy(origin); michael@0: michael@0: if (tileScreenRect.IsEmpty()) michael@0: continue; michael@0: michael@0: tileRegionRect = regionRect->Intersect(currentTileRect); michael@0: tileRegionRect.MoveBy(-currentTileRect.TopLeft()); michael@0: } michael@0: gfx::Rect rect(tileScreenRect.x, tileScreenRect.y, michael@0: tileScreenRect.width, tileScreenRect.height); michael@0: michael@0: effect->mTextureCoords = Rect(Float(tileRegionRect.x) / texRect.width, michael@0: Float(tileRegionRect.y) / texRect.height, michael@0: Float(tileRegionRect.width) / texRect.width, michael@0: Float(tileRegionRect.height) / texRect.height); michael@0: GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform); michael@0: if (usingTiles) { michael@0: DiagnosticTypes diagnostics = DIAGNOSTIC_CONTENT | DIAGNOSTIC_BIGIMAGE; michael@0: diagnostics |= iterOnWhite ? DIAGNOSTIC_COMPONENT_ALPHA : 0; michael@0: GetCompositor()->DrawDiagnostics(diagnostics, rect, aClipRect, michael@0: aTransform, mFlashCounter); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (iterOnWhite) { michael@0: iterOnWhite->NextTile(); michael@0: } michael@0: } while (usingTiles && tileIter->NextTile()); michael@0: michael@0: if (tileIter) { michael@0: tileIter->EndTileIteration(); michael@0: } michael@0: if (iterOnWhite) { michael@0: iterOnWhite->EndTileIteration(); michael@0: } michael@0: michael@0: DiagnosticTypes diagnostics = DIAGNOSTIC_CONTENT; michael@0: diagnostics |= iterOnWhite ? DIAGNOSTIC_COMPONENT_ALPHA : 0; michael@0: GetCompositor()->DrawDiagnostics(diagnostics, *aVisibleRegion, aClipRect, michael@0: aTransform, mFlashCounter); michael@0: } michael@0: michael@0: michael@0: void michael@0: ContentHostTexture::UseTextureHost(TextureHost* aTexture) michael@0: { michael@0: ContentHostBase::UseTextureHost(aTexture); michael@0: mTextureHost = aTexture; michael@0: mTextureHostOnWhite = nullptr; michael@0: } michael@0: michael@0: void michael@0: ContentHostTexture::UseComponentAlphaTextures(TextureHost* aTextureOnBlack, michael@0: TextureHost* aTextureOnWhite) michael@0: { michael@0: ContentHostBase::UseComponentAlphaTextures(aTextureOnBlack, aTextureOnWhite); michael@0: mTextureHost = aTextureOnBlack; michael@0: mTextureHostOnWhite = aTextureOnWhite; michael@0: } michael@0: michael@0: void michael@0: ContentHostTexture::SetCompositor(Compositor* aCompositor) michael@0: { michael@0: ContentHostBase::SetCompositor(aCompositor); michael@0: if (mTextureHost) { michael@0: mTextureHost->SetCompositor(aCompositor); michael@0: } michael@0: if (mTextureHostOnWhite) { michael@0: mTextureHostOnWhite->SetCompositor(aCompositor); michael@0: } michael@0: } michael@0: michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: void michael@0: ContentHostTexture::Dump(FILE* aFile, michael@0: const char* aPrefix, michael@0: bool aDumpHtml) michael@0: { michael@0: if (!aDumpHtml) { michael@0: return; michael@0: } michael@0: if (!aFile) { michael@0: aFile = stderr; michael@0: } michael@0: fprintf(aFile, "
    "); michael@0: if (mTextureHost) { michael@0: fprintf(aFile, "%s", aPrefix); michael@0: fprintf(aFile, "
  • Front buffer
  • "); michael@0: } michael@0: if (mTextureHostOnWhite) { michael@0: fprintf(aFile, "%s", aPrefix); michael@0: fprintf(aFile, "
  • Front buffer on white
  • "); michael@0: } michael@0: fprintf(aFile, "
"); michael@0: } michael@0: #endif michael@0: michael@0: static inline void michael@0: AddWrappedRegion(const nsIntRegion& aInput, nsIntRegion& aOutput, michael@0: const nsIntSize& aSize, const nsIntPoint& aShift) michael@0: { michael@0: nsIntRegion tempRegion; michael@0: tempRegion.And(nsIntRect(aShift, aSize), aInput); michael@0: tempRegion.MoveBy(-aShift); michael@0: aOutput.Or(aOutput, tempRegion); michael@0: } michael@0: michael@0: bool michael@0: ContentHostSingleBuffered::UpdateThebes(const ThebesBufferData& aData, michael@0: const nsIntRegion& aUpdated, michael@0: const nsIntRegion& aOldValidRegionBack, michael@0: nsIntRegion* aUpdatedRegionBack) michael@0: { michael@0: aUpdatedRegionBack->SetEmpty(); michael@0: michael@0: if (!mTextureHost) { michael@0: mInitialised = false; michael@0: return true; // FIXME should we return false? Returning true for now michael@0: } // to preserve existing behavior of NOT causing IPC errors. michael@0: michael@0: // updated is in screen coordinates. Convert it to buffer coordinates. michael@0: nsIntRegion destRegion(aUpdated); michael@0: destRegion.MoveBy(-aData.rect().TopLeft()); michael@0: michael@0: if (!aData.rect().Contains(aUpdated.GetBounds()) || michael@0: aData.rotation().x > aData.rect().width || michael@0: aData.rotation().y > aData.rect().height) { michael@0: NS_ERROR("Invalid update data"); michael@0: return false; michael@0: } michael@0: michael@0: // destRegion is now in logical coordinates relative to the buffer, but we michael@0: // need to account for rotation. We do that by moving the region to the michael@0: // rotation offset and then wrapping any pixels that extend off the michael@0: // bottom/right edges. michael@0: michael@0: // Shift to the rotation point michael@0: destRegion.MoveBy(aData.rotation()); michael@0: michael@0: nsIntSize bufferSize = aData.rect().Size(); michael@0: michael@0: // Select only the pixels that are still within the buffer. michael@0: nsIntRegion finalRegion; michael@0: finalRegion.And(nsIntRect(nsIntPoint(), bufferSize), destRegion); michael@0: michael@0: // For each of the overlap areas (right, bottom-right, bottom), select those michael@0: // pixels and wrap them around to the opposite edge of the buffer rect. michael@0: AddWrappedRegion(destRegion, finalRegion, bufferSize, nsIntPoint(aData.rect().width, 0)); michael@0: AddWrappedRegion(destRegion, finalRegion, bufferSize, nsIntPoint(aData.rect().width, aData.rect().height)); michael@0: AddWrappedRegion(destRegion, finalRegion, bufferSize, nsIntPoint(0, aData.rect().height)); michael@0: michael@0: MOZ_ASSERT(nsIntRect(0, 0, aData.rect().width, aData.rect().height).Contains(finalRegion.GetBounds())); michael@0: michael@0: mTextureHost->Updated(&finalRegion); michael@0: if (mTextureHostOnWhite) { michael@0: mTextureHostOnWhite->Updated(&finalRegion); michael@0: } michael@0: mInitialised = true; michael@0: michael@0: mBufferRect = aData.rect(); michael@0: mBufferRotation = aData.rotation(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentHostDoubleBuffered::UpdateThebes(const ThebesBufferData& aData, michael@0: const nsIntRegion& aUpdated, michael@0: const nsIntRegion& aOldValidRegionBack, michael@0: nsIntRegion* aUpdatedRegionBack) michael@0: { michael@0: if (!mTextureHost) { michael@0: mInitialised = false; michael@0: michael@0: *aUpdatedRegionBack = aUpdated; michael@0: return true; michael@0: } michael@0: michael@0: // We don't need to calculate an update region because we assume that if we michael@0: // are using double buffering then we have render-to-texture and thus no michael@0: // upload to do. michael@0: mTextureHost->Updated(); michael@0: if (mTextureHostOnWhite) { michael@0: mTextureHostOnWhite->Updated(); michael@0: } michael@0: mInitialised = true; michael@0: michael@0: mBufferRect = aData.rect(); michael@0: mBufferRotation = aData.rotation(); michael@0: michael@0: *aUpdatedRegionBack = aUpdated; michael@0: michael@0: // Save the current valid region of our front buffer, because if michael@0: // we're double buffering, it's going to be the valid region for the michael@0: // next back buffer sent back to the renderer. michael@0: // michael@0: // NB: we rely here on the fact that mValidRegion is initialized to michael@0: // empty, and that the first time Swap() is called we don't have a michael@0: // valid front buffer that we're going to return to content. michael@0: mValidRegionForNextBackBuffer = aOldValidRegionBack; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: ContentHostIncremental::ContentHostIncremental(const TextureInfo& aTextureInfo) michael@0: : ContentHostBase(aTextureInfo) michael@0: , mDeAllocator(nullptr) michael@0: , mLocked(false) michael@0: { michael@0: } michael@0: michael@0: ContentHostIncremental::~ContentHostIncremental() michael@0: { michael@0: } michael@0: michael@0: bool michael@0: ContentHostIncremental::CreatedIncrementalTexture(ISurfaceAllocator* aAllocator, michael@0: const TextureInfo& aTextureInfo, michael@0: const nsIntRect& aBufferRect) michael@0: { michael@0: mUpdateList.AppendElement(new TextureCreationRequest(aTextureInfo, michael@0: aBufferRect)); michael@0: mDeAllocator = aAllocator; michael@0: FlushUpdateQueue(); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: ContentHostIncremental::UpdateIncremental(TextureIdentifier aTextureId, michael@0: SurfaceDescriptor& aSurface, michael@0: const nsIntRegion& aUpdated, michael@0: const nsIntRect& aBufferRect, michael@0: const nsIntPoint& aBufferRotation) michael@0: { michael@0: mUpdateList.AppendElement(new TextureUpdateRequest(mDeAllocator, michael@0: aTextureId, michael@0: aSurface, michael@0: aUpdated, michael@0: aBufferRect, michael@0: aBufferRotation)); michael@0: FlushUpdateQueue(); michael@0: } michael@0: michael@0: void michael@0: ContentHostIncremental::FlushUpdateQueue() michael@0: { michael@0: // If we're not compositing for some reason (the window being minimized michael@0: // is one example), then we never process these updates and it can consume michael@0: // huge amounts of memory. Instead we forcibly process the updates (during the michael@0: // transaction) if the list gets too long. michael@0: static const uint32_t kMaxUpdateCount = 6; michael@0: if (mUpdateList.Length() >= kMaxUpdateCount) { michael@0: ProcessTextureUpdates(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: ContentHostIncremental::ProcessTextureUpdates() michael@0: { michael@0: for (uint32_t i = 0; i < mUpdateList.Length(); i++) { michael@0: mUpdateList[i]->Execute(this); michael@0: } michael@0: mUpdateList.Clear(); michael@0: } michael@0: michael@0: NewTextureSource* michael@0: ContentHostIncremental::GetTextureSource() michael@0: { michael@0: MOZ_ASSERT(mLocked); michael@0: return mSource; michael@0: } michael@0: michael@0: NewTextureSource* michael@0: ContentHostIncremental::GetTextureSourceOnWhite() michael@0: { michael@0: MOZ_ASSERT(mLocked); michael@0: return mSourceOnWhite; michael@0: } michael@0: michael@0: void michael@0: ContentHostIncremental::TextureCreationRequest::Execute(ContentHostIncremental* aHost) michael@0: { michael@0: Compositor* compositor = aHost->GetCompositor(); michael@0: MOZ_ASSERT(compositor); michael@0: michael@0: RefPtr temp = michael@0: compositor->CreateDataTextureSource(mTextureInfo.mTextureFlags); michael@0: MOZ_ASSERT(temp->AsSourceOGL() && michael@0: temp->AsSourceOGL()->AsTextureImageTextureSource()); michael@0: RefPtr newSource = michael@0: temp->AsSourceOGL()->AsTextureImageTextureSource(); michael@0: michael@0: RefPtr newSourceOnWhite; michael@0: if (mTextureInfo.mTextureFlags & TEXTURE_COMPONENT_ALPHA) { michael@0: temp = michael@0: compositor->CreateDataTextureSource(mTextureInfo.mTextureFlags); michael@0: MOZ_ASSERT(temp->AsSourceOGL() && michael@0: temp->AsSourceOGL()->AsTextureImageTextureSource()); michael@0: newSourceOnWhite = temp->AsSourceOGL()->AsTextureImageTextureSource(); michael@0: } michael@0: michael@0: if (mTextureInfo.mDeprecatedTextureHostFlags & TEXTURE_HOST_COPY_PREVIOUS) { michael@0: MOZ_ASSERT(aHost->mSource); michael@0: MOZ_ASSERT(aHost->mSource->IsValid()); michael@0: nsIntRect bufferRect = aHost->mBufferRect; michael@0: nsIntPoint bufferRotation = aHost->mBufferRotation; michael@0: nsIntRect overlap; michael@0: michael@0: // The buffer looks like: michael@0: // ______ michael@0: // |1 |2 | Where the center point is offset by mBufferRotation from the top-left corner. michael@0: // |___|__| michael@0: // |3 |4 | michael@0: // |___|__| michael@0: // michael@0: // This is drawn to the screen as: michael@0: // ______ michael@0: // |4 |3 | Where the center point is { width - mBufferRotation.x, height - mBufferRotation.y } from michael@0: // |___|__| from the top left corner - rotationPoint. michael@0: // |2 |1 | michael@0: // |___|__| michael@0: // michael@0: michael@0: // The basic idea below is to take all quadrant rectangles from the src and transform them into rectangles michael@0: // in the destination. Unfortunately, it seems it is overly complex and could perhaps be simplified. michael@0: michael@0: nsIntRect srcBufferSpaceBottomRight(bufferRotation.x, bufferRotation.y, bufferRect.width - bufferRotation.x, bufferRect.height - bufferRotation.y); michael@0: nsIntRect srcBufferSpaceTopRight(bufferRotation.x, 0, bufferRect.width - bufferRotation.x, bufferRotation.y); michael@0: nsIntRect srcBufferSpaceTopLeft(0, 0, bufferRotation.x, bufferRotation.y); michael@0: nsIntRect srcBufferSpaceBottomLeft(0, bufferRotation.y, bufferRotation.x, bufferRect.height - bufferRotation.y); michael@0: michael@0: overlap.IntersectRect(bufferRect, mBufferRect); michael@0: michael@0: nsIntRect srcRect(overlap), dstRect(overlap); michael@0: srcRect.MoveBy(- bufferRect.TopLeft() + bufferRotation); michael@0: michael@0: nsIntRect srcRectDrawTopRight(srcRect); michael@0: nsIntRect srcRectDrawTopLeft(srcRect); michael@0: nsIntRect srcRectDrawBottomLeft(srcRect); michael@0: // transform into the different quadrants michael@0: srcRectDrawTopRight .MoveBy(-nsIntPoint(0, bufferRect.height)); michael@0: srcRectDrawTopLeft .MoveBy(-nsIntPoint(bufferRect.width, bufferRect.height)); michael@0: srcRectDrawBottomLeft.MoveBy(-nsIntPoint(bufferRect.width, 0)); michael@0: michael@0: // Intersect with the quadrant michael@0: srcRect = srcRect .Intersect(srcBufferSpaceBottomRight); michael@0: srcRectDrawTopRight = srcRectDrawTopRight .Intersect(srcBufferSpaceTopRight); michael@0: srcRectDrawTopLeft = srcRectDrawTopLeft .Intersect(srcBufferSpaceTopLeft); michael@0: srcRectDrawBottomLeft = srcRectDrawBottomLeft.Intersect(srcBufferSpaceBottomLeft); michael@0: michael@0: dstRect = srcRect; michael@0: nsIntRect dstRectDrawTopRight(srcRectDrawTopRight); michael@0: nsIntRect dstRectDrawTopLeft(srcRectDrawTopLeft); michael@0: nsIntRect dstRectDrawBottomLeft(srcRectDrawBottomLeft); michael@0: michael@0: // transform back to src buffer space michael@0: dstRect .MoveBy(-bufferRotation); michael@0: dstRectDrawTopRight .MoveBy(-bufferRotation + nsIntPoint(0, bufferRect.height)); michael@0: dstRectDrawTopLeft .MoveBy(-bufferRotation + nsIntPoint(bufferRect.width, bufferRect.height)); michael@0: dstRectDrawBottomLeft.MoveBy(-bufferRotation + nsIntPoint(bufferRect.width, 0)); michael@0: michael@0: // transform back to draw coordinates michael@0: dstRect .MoveBy(bufferRect.TopLeft()); michael@0: dstRectDrawTopRight .MoveBy(bufferRect.TopLeft()); michael@0: dstRectDrawTopLeft .MoveBy(bufferRect.TopLeft()); michael@0: dstRectDrawBottomLeft.MoveBy(bufferRect.TopLeft()); michael@0: michael@0: // transform to destBuffer space michael@0: dstRect .MoveBy(-mBufferRect.TopLeft()); michael@0: dstRectDrawTopRight .MoveBy(-mBufferRect.TopLeft()); michael@0: dstRectDrawTopLeft .MoveBy(-mBufferRect.TopLeft()); michael@0: dstRectDrawBottomLeft.MoveBy(-mBufferRect.TopLeft()); michael@0: michael@0: newSource->EnsureBuffer(mBufferRect.Size(), michael@0: ContentForFormat(aHost->mSource->GetFormat())); michael@0: michael@0: aHost->mSource->CopyTo(srcRect, newSource, dstRect); michael@0: if (bufferRotation != nsIntPoint(0, 0)) { michael@0: // Draw the remaining quadrants. We call BlitTextureImage 3 extra michael@0: // times instead of doing a single draw call because supporting that michael@0: // with a tiled source is quite tricky. michael@0: michael@0: if (!srcRectDrawTopRight.IsEmpty()) michael@0: aHost->mSource->CopyTo(srcRectDrawTopRight, michael@0: newSource, dstRectDrawTopRight); michael@0: if (!srcRectDrawTopLeft.IsEmpty()) michael@0: aHost->mSource->CopyTo(srcRectDrawTopLeft, michael@0: newSource, dstRectDrawTopLeft); michael@0: if (!srcRectDrawBottomLeft.IsEmpty()) michael@0: aHost->mSource->CopyTo(srcRectDrawBottomLeft, michael@0: newSource, dstRectDrawBottomLeft); michael@0: } michael@0: michael@0: if (newSourceOnWhite) { michael@0: newSourceOnWhite->EnsureBuffer(mBufferRect.Size(), michael@0: ContentForFormat(aHost->mSourceOnWhite->GetFormat())); michael@0: aHost->mSourceOnWhite->CopyTo(srcRect, newSourceOnWhite, dstRect); michael@0: if (bufferRotation != nsIntPoint(0, 0)) { michael@0: // draw the remaining quadrants michael@0: if (!srcRectDrawTopRight.IsEmpty()) michael@0: aHost->mSourceOnWhite->CopyTo(srcRectDrawTopRight, michael@0: newSourceOnWhite, dstRectDrawTopRight); michael@0: if (!srcRectDrawTopLeft.IsEmpty()) michael@0: aHost->mSourceOnWhite->CopyTo(srcRectDrawTopLeft, michael@0: newSourceOnWhite, dstRectDrawTopLeft); michael@0: if (!srcRectDrawBottomLeft.IsEmpty()) michael@0: aHost->mSourceOnWhite->CopyTo(srcRectDrawBottomLeft, michael@0: newSourceOnWhite, dstRectDrawBottomLeft); michael@0: } michael@0: } michael@0: } michael@0: michael@0: aHost->mSource = newSource; michael@0: aHost->mSourceOnWhite = newSourceOnWhite; michael@0: michael@0: aHost->mBufferRect = mBufferRect; michael@0: aHost->mBufferRotation = nsIntPoint(); michael@0: } michael@0: michael@0: nsIntRect michael@0: ContentHostIncremental::TextureUpdateRequest::GetQuadrantRectangle(XSide aXSide, michael@0: YSide aYSide) const michael@0: { michael@0: // quadrantTranslation is the amount we translate the top-left michael@0: // of the quadrant by to get coordinates relative to the layer michael@0: nsIntPoint quadrantTranslation = -mBufferRotation; michael@0: quadrantTranslation.x += aXSide == LEFT ? mBufferRect.width : 0; michael@0: quadrantTranslation.y += aYSide == TOP ? mBufferRect.height : 0; michael@0: return mBufferRect + quadrantTranslation; michael@0: } michael@0: michael@0: void michael@0: ContentHostIncremental::TextureUpdateRequest::Execute(ContentHostIncremental* aHost) michael@0: { michael@0: nsIntRect drawBounds = mUpdated.GetBounds(); michael@0: michael@0: aHost->mBufferRect = mBufferRect; michael@0: aHost->mBufferRotation = mBufferRotation; michael@0: michael@0: // Figure out which quadrant to draw in michael@0: int32_t xBoundary = mBufferRect.XMost() - mBufferRotation.x; michael@0: int32_t yBoundary = mBufferRect.YMost() - mBufferRotation.y; michael@0: XSide sideX = drawBounds.XMost() <= xBoundary ? RIGHT : LEFT; michael@0: YSide sideY = drawBounds.YMost() <= yBoundary ? BOTTOM : TOP; michael@0: nsIntRect quadrantRect = GetQuadrantRectangle(sideX, sideY); michael@0: NS_ASSERTION(quadrantRect.Contains(drawBounds), "Messed up quadrants"); michael@0: michael@0: mUpdated.MoveBy(-nsIntPoint(quadrantRect.x, quadrantRect.y)); michael@0: michael@0: IntPoint offset = ToIntPoint(-mUpdated.GetBounds().TopLeft()); michael@0: michael@0: RefPtr surf = GetSurfaceForDescriptor(mDescriptor); michael@0: michael@0: if (mTextureId == TextureFront) { michael@0: aHost->mSource->Update(surf, &mUpdated, &offset); michael@0: } else { michael@0: aHost->mSourceOnWhite->Update(surf, &mUpdated, &offset); michael@0: } michael@0: } michael@0: michael@0: void michael@0: ContentHostTexture::PrintInfo(nsACString& aTo, const char* aPrefix) michael@0: { michael@0: aTo += aPrefix; michael@0: aTo += nsPrintfCString("ContentHost (0x%p)", this); michael@0: michael@0: AppendToString(aTo, mBufferRect, " [buffer-rect=", "]"); michael@0: AppendToString(aTo, mBufferRotation, " [buffer-rotation=", "]"); michael@0: if (PaintWillResample()) { michael@0: aTo += " [paint-will-resample]"; michael@0: } michael@0: michael@0: nsAutoCString pfx(aPrefix); michael@0: pfx += " "; michael@0: michael@0: if (mTextureHost) { michael@0: aTo += "\n"; michael@0: mTextureHost->PrintInfo(aTo, pfx.get()); michael@0: } michael@0: } michael@0: michael@0: michael@0: LayerRenderState michael@0: ContentHostTexture::GetRenderState() michael@0: { michael@0: if (!mTextureHost) { michael@0: return LayerRenderState(); michael@0: } michael@0: michael@0: LayerRenderState result = mTextureHost->GetRenderState(); michael@0: michael@0: if (mBufferRotation != nsIntPoint()) { michael@0: result.mFlags |= LAYER_RENDER_STATE_BUFFER_ROTATION; michael@0: } michael@0: result.SetOffset(GetOriginOffset()); michael@0: return result; michael@0: } michael@0: michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: TemporaryRef michael@0: ContentHostTexture::GetAsSurface() michael@0: { michael@0: if (!mTextureHost) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return mTextureHost->GetAsSurface(); michael@0: } michael@0: michael@0: #endif michael@0: michael@0: michael@0: } // namespace michael@0: } // namespace