void ContentClientDoubleBuffered::UpdateDestinationFrom(const RotatedBuffer& aSource, const nsIntRegion& aUpdateRegion) { nsRefPtr<gfxContext> destCtx = GetContextForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_BLACK); destCtx->SetOperator(gfxContext::OPERATOR_SOURCE); bool isClippingCheap = IsClippingCheap(destCtx, aUpdateRegion); if (isClippingCheap) { gfxUtils::ClipToRegion(destCtx, aUpdateRegion); } if (SupportsAzureContent()) { MOZ_ASSERT(!destCtx->IsCairo()); if (destCtx->GetDrawTarget()->GetFormat() == FORMAT_B8G8R8A8) { destCtx->GetDrawTarget()->ClearRect(Rect(0, 0, mFrontBufferRect.width, mFrontBufferRect.height)); } aSource.DrawBufferWithRotation(destCtx->GetDrawTarget(), BUFFER_BLACK); } else { aSource.DrawBufferWithRotation(destCtx, BUFFER_BLACK); } if (aSource.HaveBufferOnWhite()) { MOZ_ASSERT(HaveBufferOnWhite()); nsRefPtr<gfxContext> destCtx = GetContextForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_WHITE); destCtx->SetOperator(gfxContext::OPERATOR_SOURCE); bool isClippingCheap = IsClippingCheap(destCtx, aUpdateRegion); if (isClippingCheap) { gfxUtils::ClipToRegion(destCtx, aUpdateRegion); } if (SupportsAzureContent()) { MOZ_ASSERT(!destCtx->IsCairo()); if (destCtx->GetDrawTarget()->GetFormat() == FORMAT_B8G8R8A8) { destCtx->GetDrawTarget()->ClearRect(Rect(0, 0, mFrontBufferRect.width, mFrontBufferRect.height)); } aSource.DrawBufferWithRotation(destCtx->GetDrawTarget(), BUFFER_WHITE); } else { aSource.DrawBufferWithRotation(destCtx, BUFFER_WHITE); } } }
already_AddRefed<gfxContext> RotatedContentBuffer::GetContextForQuadrantUpdate(const nsIntRect& aBounds, ContextSource aSource, nsIntPoint *aTopLeft) { if (!EnsureBuffer()) { return nullptr; } nsRefPtr<gfxContext> ctx; if (aSource == BUFFER_BOTH && HaveBufferOnWhite()) { if (!EnsureBufferOnWhite()) { return nullptr; } MOZ_ASSERT(mDTBuffer && mDTBufferOnWhite); RefPtr<DrawTarget> dualDT = Factory::CreateDualDrawTarget(mDTBuffer, mDTBufferOnWhite); ctx = new gfxContext(dualDT); } else if (aSource == BUFFER_WHITE) { if (!EnsureBufferOnWhite()) { return nullptr; } ctx = new gfxContext(mDTBufferOnWhite); } else { // BUFFER_BLACK, or BUFFER_BOTH with a single buffer. ctx = new gfxContext(mDTBuffer); } // Figure out which quadrant to draw in int32_t xBoundary = mBufferRect.XMost() - mBufferRotation.x; int32_t yBoundary = mBufferRect.YMost() - mBufferRotation.y; XSide sideX = aBounds.XMost() <= xBoundary ? RIGHT : LEFT; YSide sideY = aBounds.YMost() <= yBoundary ? BOTTOM : TOP; nsIntRect quadrantRect = GetQuadrantRectangle(sideX, sideY); NS_ASSERTION(quadrantRect.Contains(aBounds), "Messed up quadrants"); ctx->Translate(-gfxPoint(quadrantRect.x, quadrantRect.y)); if (aTopLeft) { *aTopLeft = nsIntPoint(quadrantRect.x, quadrantRect.y); } return ctx.forget(); }
void ContentClientDoubleBuffered::UpdateDestinationFrom(const RotatedBuffer& aSource, const nsIntRegion& aUpdateRegion) { nsRefPtr<gfxContext> destCtx = GetContextForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_BLACK); if (!destCtx) { return; } destCtx->SetOperator(gfxContext::OPERATOR_SOURCE); bool isClippingCheap = IsClippingCheap(destCtx, aUpdateRegion); if (isClippingCheap) { gfxUtils::ClipToRegion(destCtx, aUpdateRegion); } if (SupportsAzureContent()) { MOZ_ASSERT(!destCtx->IsCairo()); aSource.DrawBufferWithRotation(destCtx->GetDrawTarget(), BUFFER_BLACK, 1.0, OP_SOURCE); } else { aSource.DrawBufferWithRotation(destCtx, BUFFER_BLACK); } if (aSource.HaveBufferOnWhite()) { MOZ_ASSERT(HaveBufferOnWhite()); nsRefPtr<gfxContext> destCtx = GetContextForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_WHITE); destCtx->SetOperator(gfxContext::OPERATOR_SOURCE); bool isClippingCheap = IsClippingCheap(destCtx, aUpdateRegion); if (isClippingCheap) { gfxUtils::ClipToRegion(destCtx, aUpdateRegion); } if (SupportsAzureContent()) { MOZ_ASSERT(!destCtx->IsCairo()); aSource.DrawBufferWithRotation(destCtx->GetDrawTarget(), BUFFER_WHITE, 1.0, OP_SOURCE); } else { aSource.DrawBufferWithRotation(destCtx, BUFFER_WHITE); } } }
void ContentClientDoubleBuffered::UpdateDestinationFrom(const RotatedBuffer& aSource, const nsIntRegion& aUpdateRegion) { DrawIterator iter; while (DrawTarget* destDT = BorrowDrawTargetForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_BLACK, &iter)) { bool isClippingCheap = IsClippingCheap(destDT, iter.mDrawRegion); if (isClippingCheap) { gfxUtils::ClipToRegion(destDT, iter.mDrawRegion); } aSource.DrawBufferWithRotation(destDT, BUFFER_BLACK, 1.0, CompositionOp::OP_SOURCE); if (isClippingCheap) { destDT->PopClip(); } // Flush the destination before the sources become inaccessible (Unlock). destDT->Flush(); ReturnDrawTargetToBuffer(destDT); } if (aSource.HaveBufferOnWhite()) { MOZ_ASSERT(HaveBufferOnWhite()); DrawIterator whiteIter; while (DrawTarget* destDT = BorrowDrawTargetForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_WHITE, &whiteIter)) { bool isClippingCheap = IsClippingCheap(destDT, whiteIter.mDrawRegion); if (isClippingCheap) { gfxUtils::ClipToRegion(destDT, whiteIter.mDrawRegion); } aSource.DrawBufferWithRotation(destDT, BUFFER_WHITE, 1.0, CompositionOp::OP_SOURCE); if (isClippingCheap) { destDT->PopClip(); } // Flush the destination before the sources become inaccessible (Unlock). destDT->Flush(); ReturnDrawTargetToBuffer(destDT); } } }
RotatedContentBuffer::PaintState RotatedContentBuffer::BeginPaint(ThebesLayer* aLayer, ContentType aContentType, uint32_t aFlags) { PaintState result; // We need to disable rotation if we're going to be resampled when // drawing, because we might sample across the rotation boundary. bool canHaveRotation = gfxPlatform::BufferRotationEnabled() && !(aFlags & (PAINT_WILL_RESAMPLE | PAINT_NO_ROTATION)); nsIntRegion validRegion = aLayer->GetValidRegion(); Layer::SurfaceMode mode; ContentType contentType; nsIntRegion neededRegion; bool canReuseBuffer; nsIntRect destBufferRect; while (true) { mode = aLayer->GetSurfaceMode(); contentType = aContentType; neededRegion = aLayer->GetVisibleRegion(); canReuseBuffer = HaveBuffer() && BufferSizeOkFor(neededRegion.GetBounds().Size()); if (canReuseBuffer) { if (mBufferRect.Contains(neededRegion.GetBounds())) { // We don't need to adjust mBufferRect. destBufferRect = mBufferRect; } else if (neededRegion.GetBounds().Size() <= mBufferRect.Size()) { // The buffer's big enough but doesn't contain everything that's // going to be visible. We'll move it. destBufferRect = nsIntRect(neededRegion.GetBounds().TopLeft(), mBufferRect.Size()); } else { destBufferRect = neededRegion.GetBounds(); } } else { // We won't be reusing the buffer. Compute a new rect. destBufferRect = ComputeBufferRect(neededRegion.GetBounds()); } if (mode == Layer::SURFACE_COMPONENT_ALPHA) { #if defined(MOZ_GFX_OPTIMIZE_MOBILE) || defined(MOZ_WIDGET_GONK) mode = Layer::SURFACE_SINGLE_CHANNEL_ALPHA; #else if (!aLayer->GetParent() || !aLayer->GetParent()->SupportsComponentAlphaChildren() || !aLayer->Manager()->IsCompositingCheap() || !aLayer->AsShadowableLayer() || !aLayer->AsShadowableLayer()->HasShadow() || !gfxPlatform::ComponentAlphaEnabled()) { mode = Layer::SURFACE_SINGLE_CHANNEL_ALPHA; } else { contentType = GFX_CONTENT_COLOR; } #endif } if ((aFlags & PAINT_WILL_RESAMPLE) && (!neededRegion.GetBounds().IsEqualInterior(destBufferRect) || neededRegion.GetNumRects() > 1)) { // The area we add to neededRegion might not be painted opaquely if (mode == Layer::SURFACE_OPAQUE) { contentType = GFX_CONTENT_COLOR_ALPHA; mode = Layer::SURFACE_SINGLE_CHANNEL_ALPHA; } // We need to validate the entire buffer, to make sure that only valid // pixels are sampled neededRegion = destBufferRect; } // If we have an existing buffer, but the content type has changed or we // have transitioned into/out of component alpha, then we need to recreate it. if (HaveBuffer() && (contentType != BufferContentType() || (mode == Layer::SURFACE_COMPONENT_ALPHA) != HaveBufferOnWhite())) { // We're effectively clearing the valid region, so we need to draw // the entire needed region now. result.mRegionToInvalidate = aLayer->GetValidRegion(); validRegion.SetEmpty(); Clear(); // Restart decision process with the cleared buffer. We can only go // around the loop one more iteration, since mDTBuffer is null now. continue; } break; } NS_ASSERTION(destBufferRect.Contains(neededRegion.GetBounds()), "Destination rect doesn't contain what we need to paint"); result.mRegionToDraw.Sub(neededRegion, validRegion); if (result.mRegionToDraw.IsEmpty()) return result; nsIntRect drawBounds = result.mRegionToDraw.GetBounds(); RefPtr<DrawTarget> destDTBuffer; RefPtr<DrawTarget> destDTBufferOnWhite; uint32_t bufferFlags = canHaveRotation ? ALLOW_REPEAT : 0; if (mode == Layer::SURFACE_COMPONENT_ALPHA) { bufferFlags |= BUFFER_COMPONENT_ALPHA; } if (canReuseBuffer) { if (!EnsureBuffer()) { return result; } nsIntRect keepArea; if (keepArea.IntersectRect(destBufferRect, mBufferRect)) { // Set mBufferRotation so that the pixels currently in mDTBuffer // will still be rendered in the right place when mBufferRect // changes to destBufferRect. nsIntPoint newRotation = mBufferRotation + (destBufferRect.TopLeft() - mBufferRect.TopLeft()); WrapRotationAxis(&newRotation.x, mBufferRect.width); WrapRotationAxis(&newRotation.y, mBufferRect.height); NS_ASSERTION(nsIntRect(nsIntPoint(0,0), mBufferRect.Size()).Contains(newRotation), "newRotation out of bounds"); int32_t xBoundary = destBufferRect.XMost() - newRotation.x; int32_t yBoundary = destBufferRect.YMost() - newRotation.y; if ((drawBounds.x < xBoundary && xBoundary < drawBounds.XMost()) || (drawBounds.y < yBoundary && yBoundary < drawBounds.YMost()) || (newRotation != nsIntPoint(0,0) && !canHaveRotation)) { // The stuff we need to redraw will wrap around an edge of the // buffer, so move the pixels we can keep into a position that // lets us redraw in just one quadrant. if (mBufferRotation == nsIntPoint(0,0)) { nsIntRect srcRect(nsIntPoint(0, 0), mBufferRect.Size()); nsIntPoint dest = mBufferRect.TopLeft() - destBufferRect.TopLeft(); MOZ_ASSERT(mDTBuffer); mDTBuffer->CopyRect(IntRect(srcRect.x, srcRect.y, srcRect.width, srcRect.height), IntPoint(dest.x, dest.y)); if (mode == Layer::SURFACE_COMPONENT_ALPHA) { if (!EnsureBufferOnWhite()) { return result; } MOZ_ASSERT(mDTBufferOnWhite); mDTBufferOnWhite->CopyRect(IntRect(srcRect.x, srcRect.y, srcRect.width, srcRect.height), IntPoint(dest.x, dest.y)); } result.mDidSelfCopy = true; mDidSelfCopy = true; // Don't set destBuffer; we special-case self-copies, and // just did the necessary work above. mBufferRect = destBufferRect; } else { // With azure and a data surface perform an buffer unrotate // (SelfCopy). unsigned char* data; IntSize size; int32_t stride; SurfaceFormat format; if (mDTBuffer->LockBits(&data, &size, &stride, &format)) { uint8_t bytesPerPixel = BytesPerPixel(format); BufferUnrotate(data, size.width * bytesPerPixel, size.height, stride, newRotation.x * bytesPerPixel, newRotation.y); mDTBuffer->ReleaseBits(data); if (mode == Layer::SURFACE_COMPONENT_ALPHA) { if (!EnsureBufferOnWhite()) { return result; } MOZ_ASSERT(mDTBufferOnWhite); mDTBufferOnWhite->LockBits(&data, &size, &stride, &format); uint8_t bytesPerPixel = BytesPerPixel(format); BufferUnrotate(data, size.width * bytesPerPixel, size.height, stride, newRotation.x * bytesPerPixel, newRotation.y); mDTBufferOnWhite->ReleaseBits(data); } // Buffer unrotate moves all the pixels, note that // we self copied for SyncBackToFrontBuffer result.mDidSelfCopy = true; mDidSelfCopy = true; mBufferRect = destBufferRect; mBufferRotation = nsIntPoint(0, 0); } if (!result.mDidSelfCopy) { destBufferRect = ComputeBufferRect(neededRegion.GetBounds()); CreateBuffer(contentType, destBufferRect, bufferFlags, &destDTBuffer, &destDTBufferOnWhite); if (!destDTBuffer) { return result; } } } } else { mBufferRect = destBufferRect; mBufferRotation = newRotation; } } else { // No pixels are going to be kept. The whole visible region // will be redrawn, so we don't need to copy anything, so we don't // set destBuffer. mBufferRect = destBufferRect; mBufferRotation = nsIntPoint(0,0); } } else { // The buffer's not big enough, so allocate a new one CreateBuffer(contentType, destBufferRect, bufferFlags, &destDTBuffer, &destDTBufferOnWhite); if (!destDTBuffer) { return result; } } NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || destBufferRect == neededRegion.GetBounds(), "If we're resampling, we need to validate the entire buffer"); // If we have no buffered data already, then destBuffer will be a fresh buffer // and we do not need to clear it below. bool isClear = !HaveBuffer(); if (destDTBuffer) { if (!isClear && (mode != Layer::SURFACE_COMPONENT_ALPHA || HaveBufferOnWhite())) { // Copy the bits nsIntPoint offset = -destBufferRect.TopLeft(); Matrix mat; mat.Translate(offset.x, offset.y); destDTBuffer->SetTransform(mat); if (!EnsureBuffer()) { return result; } MOZ_ASSERT(mDTBuffer, "Have we got a Thebes buffer for some reason?"); DrawBufferWithRotation(destDTBuffer, BUFFER_BLACK, 1.0, OP_SOURCE); destDTBuffer->SetTransform(Matrix()); if (mode == Layer::SURFACE_COMPONENT_ALPHA) { NS_ASSERTION(destDTBufferOnWhite, "Must have a white buffer!"); destDTBufferOnWhite->SetTransform(mat); if (!EnsureBufferOnWhite()) { return result; } MOZ_ASSERT(mDTBufferOnWhite, "Have we got a Thebes buffer for some reason?"); DrawBufferWithRotation(destDTBufferOnWhite, BUFFER_WHITE, 1.0, OP_SOURCE); destDTBufferOnWhite->SetTransform(Matrix()); } } mDTBuffer = destDTBuffer.forget(); mDTBufferOnWhite = destDTBufferOnWhite.forget(); mBufferRect = destBufferRect; mBufferRotation = nsIntPoint(0,0); } NS_ASSERTION(canHaveRotation || mBufferRotation == nsIntPoint(0,0), "Rotation disabled, but we have nonzero rotation?"); nsIntRegion invalidate; invalidate.Sub(aLayer->GetValidRegion(), destBufferRect); result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate); nsIntPoint topLeft; result.mContext = GetContextForQuadrantUpdate(drawBounds, BUFFER_BOTH, &topLeft); result.mClip = CLIP_DRAW_SNAPPED; if (mode == Layer::SURFACE_COMPONENT_ALPHA) { MOZ_ASSERT(mDTBuffer && mDTBufferOnWhite); nsIntRegionRectIterator iter(result.mRegionToDraw); const nsIntRect *iterRect; while ((iterRect = iter.Next())) { mDTBuffer->FillRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height), ColorPattern(Color(0.0, 0.0, 0.0, 1.0))); mDTBufferOnWhite->FillRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0))); } } else if (contentType == GFX_CONTENT_COLOR_ALPHA && !isClear) { nsIntRegionRectIterator iter(result.mRegionToDraw); const nsIntRect *iterRect; while ((iterRect = iter.Next())) { result.mContext->GetDrawTarget()->ClearRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height)); } } return result; }