void ClientTiledPaintedLayer::RenderLayer() { LayerManager::DrawPaintedLayerCallback callback = ClientManager()->GetPaintedLayerCallback(); void *data = ClientManager()->GetPaintedLayerCallbackData(); IntSize layerSize = mVisibleRegion.GetBounds().Size(); if (mContentClient && !mContentClient->SupportsLayerSize(layerSize, ClientManager())) { ClearCachedResources(); MOZ_ASSERT(!mContentClient); } if (!mContentClient) { if (mCreationHint == LayerManager::NONE && SingleTiledContentClient::ClientSupportsLayerSize(layerSize, ClientManager()) && gfxPrefs::LayersSingleTileEnabled()) { mContentClient = new SingleTiledContentClient(this, ClientManager()); } else { mContentClient = new MultiTiledContentClient(this, ClientManager()); } mContentClient->Connect(); ClientManager()->AsShadowForwarder()->Attach(mContentClient, this); MOZ_ASSERT(mContentClient->GetForwarder()); } if (mContentClient->GetTiledBuffer()->HasFormatChanged()) { mValidRegion = nsIntRegion(); mContentClient->GetTiledBuffer()->ResetPaintedAndValidState(); } TILING_LOG("TILING %p: Initial visible region %s\n", this, Stringify(mVisibleRegion).c_str()); TILING_LOG("TILING %p: Initial valid region %s\n", this, Stringify(mValidRegion).c_str()); TILING_LOG("TILING %p: Initial low-precision valid region %s\n", this, Stringify(mLowPrecisionValidRegion).c_str()); nsIntRegion neededRegion = mVisibleRegion; #ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE // This is handled by PadDrawTargetOutFromRegion in TiledContentClient for mobile if (MayResample()) { // If we're resampling then bilinear filtering can read up to 1 pixel // outside of our texture coords. Make the visible region a single rect, // and pad it out by 1 pixel (restricted to tile boundaries) so that // we always have valid content or transparent pixels to sample from. IntRect bounds = neededRegion.GetBounds(); IntRect wholeTiles = bounds; wholeTiles.InflateToMultiple(IntSize( gfxPlatform::GetPlatform()->GetTileWidth(), gfxPlatform::GetPlatform()->GetTileHeight())); IntRect padded = bounds; padded.Inflate(1); padded.IntersectRect(padded, wholeTiles); neededRegion = padded; } #endif nsIntRegion invalidRegion; invalidRegion.Sub(neededRegion, mValidRegion); if (invalidRegion.IsEmpty()) { EndPaint(); return; } if (!callback) { ClientManager()->SetTransactionIncomplete(); return; } if (!ClientManager()->IsRepeatTransaction()) { // Only paint the mask layers on the first transaction. RenderMaskLayers(this); // For more complex cases we need to calculate a bunch of metrics before we // can do the paint. BeginPaint(); if (mPaintData.mPaintFinished) { return; } // Make sure that tiles that fall outside of the visible region or outside of the // critical displayport are discarded on the first update. Also make sure that we // only draw stuff inside the critical displayport on the first update. mValidRegion.And(mValidRegion, neededRegion); if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { mValidRegion.And(mValidRegion, mPaintData.mCriticalDisplayPort.ToUnknownRect()); invalidRegion.And(invalidRegion, mPaintData.mCriticalDisplayPort.ToUnknownRect()); } TILING_LOG("TILING %p: First-transaction valid region %s\n", this, Stringify(mValidRegion).c_str()); TILING_LOG("TILING %p: First-transaction invalid region %s\n", this, Stringify(invalidRegion).c_str()); } else { if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { invalidRegion.And(invalidRegion, mPaintData.mCriticalDisplayPort.ToUnknownRect()); } TILING_LOG("TILING %p: Repeat-transaction invalid region %s\n", this, Stringify(invalidRegion).c_str()); } nsIntRegion lowPrecisionInvalidRegion; if (mContentClient->GetLowPrecisionTiledBuffer()) { // Calculate the invalid region for the low precision buffer. Make sure // to remove the valid high-precision area so we don't double-paint it. lowPrecisionInvalidRegion.Sub(neededRegion, mLowPrecisionValidRegion); lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion); } TILING_LOG("TILING %p: Low-precision invalid region %s\n", this, Stringify(lowPrecisionInvalidRegion).c_str()); bool updatedHighPrecision = RenderHighPrecision(invalidRegion, neededRegion, callback, data); if (updatedHighPrecision) { ClientManager()->Hold(this); mContentClient->UpdatedBuffer(TiledContentClient::TILED_BUFFER); if (!mPaintData.mPaintFinished) { // There is still more high-res stuff to paint, so we're not // done yet. A subsequent transaction will take care of this. ClientManager()->SetRepeatTransaction(); return; } } // If there is nothing to draw in low-precision, then we're done. if (lowPrecisionInvalidRegion.IsEmpty()) { EndPaint(); return; } if (updatedHighPrecision) { // If there are low precision updates, but we just did some high-precision // updates, then mark the paint as unfinished and request a repeat transaction. // This is so that we don't perform low-precision updates in the same transaction // as high-precision updates. TILING_LOG("TILING %p: Scheduling repeat transaction for low-precision painting\n", this); ClientManager()->SetRepeatTransaction(); mPaintData.mLowPrecisionPaintCount = 1; mPaintData.mPaintFinished = false; return; } bool updatedLowPrecision = RenderLowPrecision(lowPrecisionInvalidRegion, neededRegion, callback, data); if (updatedLowPrecision) { ClientManager()->Hold(this); mContentClient->UpdatedBuffer(TiledContentClient::LOW_PRECISION_TILED_BUFFER); if (!mPaintData.mPaintFinished) { // There is still more low-res stuff to paint, so we're not // done yet. A subsequent transaction will take care of this. ClientManager()->SetRepeatTransaction(); return; } } // If we get here, we've done all the high- and low-precision // paints we wanted to do, so we can finish the paint and chill. EndPaint(); }
/* static */ void gfxAlphaBoxBlur::BlurRectangle(gfxContext* aDestinationCtx, const gfxRect& aRect, RectCornerRadii* aCornerRadii, const gfxPoint& aBlurStdDev, const gfxRGBA& aShadowColor, const gfxRect& aDirtyRect, const gfxRect& aSkipRect) { DrawTarget& destDrawTarget = *aDestinationCtx->GetDrawTarget(); IntSize blurRadius = CalculateBlurRadius(aBlurStdDev); IntRect rect = RoundedToInt(ToRect(aRect)); IntMargin extendDestBy; IntMargin slice; RefPtr<SourceSurface> boxShadow = GetBlur(destDrawTarget, rect.Size(), blurRadius, aCornerRadii, aShadowColor, extendDestBy, slice); if (!boxShadow) { return; } destDrawTarget.PushClipRect(ToRect(aDirtyRect)); // Copy the right parts from boxShadow into destDrawTarget. The middle parts // will be stretched, border-image style. Rect srcOuter(Point(), Size(boxShadow->GetSize())); Rect srcInner = srcOuter; srcInner.Deflate(Margin(slice)); rect.Inflate(extendDestBy); Rect dstOuter(rect); Rect dstInner(rect); dstInner.Deflate(Margin(slice)); Rect skipRect = ToRect(aSkipRect); if (srcInner.IsEqualInterior(srcOuter)) { MOZ_ASSERT(dstInner.IsEqualInterior(dstOuter)); // The target rect is smaller than the minimal size so just draw the surface destDrawTarget.DrawSurface(boxShadow, dstInner, srcInner); } else { DrawBoxShadows(destDrawTarget, boxShadow, dstOuter, dstInner, srcOuter, srcInner, skipRect); // Middle part RepeatOrStretchSurface(destDrawTarget, boxShadow, RectWithEdgesTRBL(dstInner.Y(), dstInner.XMost(), dstInner.YMost(), dstInner.X()), RectWithEdgesTRBL(srcInner.Y(), srcInner.XMost(), srcInner.YMost(), srcInner.X()), skipRect); } // A note about anti-aliasing and seems between adjacent parts: // We don't explicitly disable anti-aliasing in the DrawSurface calls above, // so if there's a transform on destDrawTarget that is not pixel-aligned, // there will be seams between adjacent parts of the box-shadow. It's hard to // avoid those without the use of an intermediate surface. // You might think that we could avoid those by just turning of AA, but there // is a problem with that: Box-shadow rendering needs to clip out the // element's border box, and we'd like that clip to have anti-aliasing - // especially if the element has rounded corners! So we can't do that unless // we have a way to say "Please anti-alias the clip, but don't antialias the // destination rect of the DrawSurface call". // On OS X there is an additional problem with turning off AA: CoreGraphics // will not just fill the pixels that have their pixel center inside the // filled shape. Instead, it will fill all the pixels which are partially // covered by the shape. So for pixels on the edge between two adjacent parts, // all those pixels will be painted to by both parts, which looks very bad. destDrawTarget.PopClip(); }