void DrawTargetD2D1::DrawSurface(SourceSurface *aSurface, const Rect &aDest, const Rect &aSource, const DrawSurfaceOptions &aSurfOptions, const DrawOptions &aOptions) { RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, ExtendMode::CLAMP); if (!image) { gfxWarning() << *this << ": Unable to get D2D image for surface."; return; } PrepareForDrawing(aOptions.mCompositionOp, ColorPattern(Color())); D2D1_RECT_F samplingBounds; if (aSurfOptions.mSamplingBounds == SamplingBounds::BOUNDED) { samplingBounds = D2DRect(aSource); } else { samplingBounds = D2D1::RectF(0, 0, Float(aSurface->GetSize().width), Float(aSurface->GetSize().height)); } Float xScale = aDest.width / aSource.width; Float yScale = aDest.height / aSource.height; RefPtr<ID2D1ImageBrush> brush; // Here we scale the source pattern up to the size and position where we want // it to be. Matrix transform; transform.Translate(aDest.x, aDest.y); transform.Scale(xScale, yScale); mDC->CreateImageBrush(image, D2D1::ImageBrushProperties(samplingBounds), D2D1::BrushProperties(aOptions.mAlpha, D2DMatrix(transform)), byRef(brush)); mDC->FillRectangle(D2DRect(aDest), brush); FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(Color())); }
static void FillSurface(DrawTarget* aDT, const nsIntRegion& aRegion, const nsIntPoint& aOffset, const gfxRGBA& aColor) { nsIntRegionRectIterator iter(aRegion); const nsIntRect* r; while ((r = iter.Next()) != nullptr) { aDT->FillRect(Rect(r->x - aOffset.x, r->y - aOffset.y, r->width, r->height), ColorPattern(ToColor(aColor))); } }
bool TextureClientD3D11::Lock(OpenMode aMode) { if (!IsAllocated()) { return false; } MOZ_ASSERT(!mIsLocked, "The Texture is already locked!"); if (mTexture) { MOZ_ASSERT(!mTexture10); mIsLocked = LockD3DTexture(mTexture.get()); } else { MOZ_ASSERT(!mTexture); mIsLocked = LockD3DTexture(mTexture10.get()); } if (!mIsLocked) { return false; } if (NS_IsMainThread()) { // Make sure that successful write-lock means we will have a DrawTarget to // write into. if (aMode & OpenMode::OPEN_WRITE) { mDrawTarget = BorrowDrawTarget(); if (!mDrawTarget) { Unlock(); return false; } } if (mNeedsClear) { mDrawTarget = BorrowDrawTarget(); if (!mDrawTarget) { Unlock(); return false; } mDrawTarget->ClearRect(Rect(0, 0, GetSize().width, GetSize().height)); mNeedsClear = false; } if (mNeedsClearWhite) { mDrawTarget = BorrowDrawTarget(); if (!mDrawTarget) { Unlock(); return false; } mDrawTarget->FillRect(Rect(0, 0, GetSize().width, GetSize().height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0))); mNeedsClearWhite = false; } } return true; }
void DrawTargetD2D1::DrawFilter(FilterNode *aNode, const Rect &aSourceRect, const Point &aDestPoint, const DrawOptions &aOptions) { if (aNode->GetBackendType() != FILTER_BACKEND_DIRECT2D1_1) { gfxWarning() << *this << ": Incompatible filter passed to DrawFilter."; return; } PrepareForDrawing(aOptions.mCompositionOp, ColorPattern(Color())); mDC->DrawImage(static_cast<FilterNodeD2D1*>(aNode)->OutputEffect(), D2DPoint(aDestPoint), D2DRect(aSourceRect)); }
void BasicCompositor::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(); if (mTarget || 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 = 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 (!mTarget) { mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion); } mDrawTarget = nullptr; mRenderTarget = nullptr; }
void BasicCompositor::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(ToRect(mInvalidRegion.GetBounds()), ColorPattern(Color(r, g, b, 0.2f))); } // Pop aInvalidregion mRenderTarget->mDrawTarget->PopClip(); // 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 = 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. nsIntRegionRectIterator iter(mInvalidRegion); for (const nsIntRect *r = iter.Next(); r; r = iter.Next()) { dest->CopySurface(source, IntRect(r->x - mInvalidRect.x, r->y - mInvalidRect.y, r->width, r->height), IntPoint(r->x - offset.x, r->y - offset.y)); } if (!mTarget) { mWidget->EndRemoteDrawing(); } mDrawTarget = nullptr; mRenderTarget = nullptr; }
void ClientSingleTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion, const nsIntRegion& aDirtyRegion, LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData) { // Compare layer visible region size to current backbuffer size, discard if not matching. IntSize size = mPaintedLayer->GetVisibleRegion().GetBounds().Size(); IntPoint origin = mPaintedLayer->GetVisibleRegion().GetBounds().TopLeft(); nsIntRegion paintRegion = aPaintRegion; if (mSize != size || mTilingOrigin != origin) { ResetPaintedAndValidState(); mSize = size; mTilingOrigin = origin; paintRegion = aNewValidRegion; } SurfaceMode mode; gfxContentType content = GetContentType(&mode); mFormat = gfxPlatform::GetPlatform()->OptimalFormatForContent(content); if (mTile.IsPlaceholderTile()) { mTile.SetLayerManager(mManager); mTile.SetTextureAllocator(this); } mTile.SetCompositableClient(mCompositableClient); // The dirty region relative to the top-left of the tile. nsIntRegion tileDirtyRegion = paintRegion.MovedBy(-mTilingOrigin); nsIntRegion extraPainted; RefPtr<TextureClient> backBufferOnWhite; RefPtr<TextureClient> backBuffer = mTile.GetBackBuffer(tileDirtyRegion, content, mode, extraPainted, &backBufferOnWhite); mTile.mUpdateRect = tileDirtyRegion.GetBounds().Union(extraPainted.GetBounds()); extraPainted.MoveBy(mTilingOrigin); extraPainted.And(extraPainted, aNewValidRegion); mPaintedRegion.OrWith(paintRegion); mPaintedRegion.OrWith(extraPainted); if (!backBuffer) { return; } RefPtr<DrawTarget> dt = backBuffer->BorrowDrawTarget(); RefPtr<DrawTarget> dtOnWhite; if (backBufferOnWhite) { dtOnWhite = backBufferOnWhite->BorrowDrawTarget(); } if (mode != SurfaceMode::SURFACE_OPAQUE) { nsIntRegionRectIterator iter(tileDirtyRegion); const IntRect *iterRect; while ((iterRect = iter.Next())) { if (dtOnWhite) { dt->FillRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height), ColorPattern(Color(0.0, 0.0, 0.0, 1.0))); dtOnWhite->FillRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0))); } else { dt->ClearRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height)); } } } if (dtOnWhite) { dt = Factory::CreateDualDrawTarget(dt, dtOnWhite); dtOnWhite = nullptr; } { nsRefPtr<gfxContext> ctx = new gfxContext(dt); ctx->SetMatrix(ctx->CurrentMatrix().Translate(-mTilingOrigin.x, -mTilingOrigin.y)); aCallback(mPaintedLayer, ctx, paintRegion, &paintRegion, DrawRegionClip::DRAW, nsIntRegion(), aCallbackData); } // Mark the area we just drew into the back buffer as invalid in the front buffer as they're // now out of sync. mTile.mInvalidFront.OrWith(tileDirtyRegion); // The new buffer is now validated, remove the dirty region from it. mTile.mInvalidBack.SubOut(tileDirtyRegion); dt = nullptr; mTile.Flip(); UnlockTile(mTile); if (backBuffer->HasInternalBuffer()) { // If our new buffer has an internal buffer, we don't want to keep another // TextureClient around unnecessarily, so discard the back-buffer. mTile.DiscardBackBuffer(); } mValidRegion = aNewValidRegion; mLastPaintSurfaceMode = mode; mLastPaintContentType = content; }
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; }