static void RepeatOrStretchSurface(DrawTarget& aDT, SourceSurface* aSurface, const Rect& aDest, const Rect& aSrc, Rect& aSkipRect) { if (aSkipRect.Contains(aDest)) { return; } if ((!aDT.GetTransform().IsRectilinear() && aDT.GetBackendType() != BackendType::CAIRO) || (aDT.GetBackendType() == BackendType::DIRECT2D)) { // Use stretching if possible, since it leads to less seams when the // destination is transformed. However, don't do this if we're using cairo, // because if cairo is using pixman it won't render anything for large // stretch factors because pixman's internal fixed point precision is not // high enough to handle those scale factors. // Calling FillRect on a D2D backend with a repeating pattern is much slower // than DrawSurface, so special case the D2D backend here. aDT.DrawSurface(aSurface, aDest, aSrc); return; } SurfacePattern pattern(aSurface, ExtendMode::REPEAT, Matrix::Translation(aDest.TopLeft() - aSrc.TopLeft()), Filter::GOOD, RoundedToInt(aSrc)); aDT.FillRect(aDest, pattern); }
void ClientLayerManager::MakeSnapshotIfRequired() { if (!mShadowTarget) { return; } if (mWidget) { if (CompositorChild* remoteRenderer = GetRemoteRenderer()) { nsIntRect bounds = ToOutsideIntRect(mShadowTarget->GetClipExtents()); SurfaceDescriptor inSnapshot; if (!bounds.IsEmpty() && mForwarder->AllocSurfaceDescriptor(bounds.Size().ToIntSize(), gfxContentType::COLOR_ALPHA, &inSnapshot) && remoteRenderer->SendMakeSnapshot(inSnapshot, bounds)) { RefPtr<DataSourceSurface> surf = GetSurfaceForDescriptor(inSnapshot); DrawTarget* dt = mShadowTarget->GetDrawTarget(); Rect dstRect(bounds.x, bounds.y, bounds.width, bounds.height); Rect srcRect(0, 0, bounds.width, bounds.height); dt->DrawSurface(surf, dstRect, srcRect, DrawSurfaceOptions(), DrawOptions(1.0f, CompositionOp::OP_OVER)); } mForwarder->DestroySharedSurface(&inSnapshot); } } mShadowTarget = nullptr; }
void ClientLayerManager::MakeSnapshotIfRequired() { if (!mShadowTarget) { return; } if (mWidget) { if (CompositorChild* remoteRenderer = GetRemoteRenderer()) { nsIntRect bounds; mWidget->GetBounds(bounds); IntSize widgetSize = bounds.Size().ToIntSize(); SurfaceDescriptor inSnapshot, snapshot; if (mForwarder->AllocSurfaceDescriptor(widgetSize, gfxContentType::COLOR_ALPHA, &inSnapshot) && // The compositor will usually reuse |snapshot| and return // it through |outSnapshot|, but if it doesn't, it's // responsible for freeing |snapshot|. remoteRenderer->SendMakeSnapshot(inSnapshot, &snapshot)) { RefPtr<DataSourceSurface> surf = GetSurfaceForDescriptor(snapshot); DrawTarget* dt = mShadowTarget->GetDrawTarget(); Rect widgetRect(Point(0, 0), Size(widgetSize.width, widgetSize.height)); dt->DrawSurface(surf, widgetRect, widgetRect, DrawSurfaceOptions(), DrawOptions(1.0f, CompositionOp::OP_OVER)); } if (IsSurfaceDescriptorValid(snapshot)) { mForwarder->DestroySharedSurface(&snapshot); } } } mShadowTarget = nullptr; }
void gfxQuartzNativeDrawing::EndNativeDrawing() { NS_ASSERTION(mCGContext, "EndNativeDrawing called without BeginNativeDrawing"); MOZ_ASSERT(!mContext->IsCairo(), "BeginNativeDrawing succeeded with cairo context?"); mBorrowedContext.Finish(); if (mDrawTarget) { DrawTarget *dest = mContext->GetDrawTarget(); RefPtr<SourceSurface> source = mDrawTarget->Snapshot(); IntSize backingSize(NSToIntFloor(mNativeRect.width * mBackingScale), NSToIntFloor(mNativeRect.height * mBackingScale)); Matrix oldTransform = dest->GetTransform(); Matrix newTransform = oldTransform; newTransform.Translate(mNativeRect.x, mNativeRect.y); newTransform.Scale(1.0f / mBackingScale, 1.0f / mBackingScale); dest->SetTransform(newTransform); dest->DrawSurface(source, gfx::Rect(0, 0, backingSize.width, backingSize.height), gfx::Rect(0, 0, backingSize.width, backingSize.height)); dest->SetTransform(oldTransform); } }
/*** * Blur an inset box shadow by doing: * 1) Create a minimal box shadow path that creates a frame. * 2) Draw the box shadow portion over the destination surface. * 3) The "inset" part is created by a clip rect that properly clips * the alpha mask so that it has clean edges. We still create the full * proper alpha mask, but let the clip deal with the clean edges. * * All parameters should already be in device pixels. */ void gfxAlphaBoxBlur::BlurInsetBox(gfxContext* aDestinationCtx, const Rect aDestinationRect, const Rect aShadowClipRect, const gfxIntSize aBlurRadius, const gfxIntSize aSpreadRadius, const Color& aShadowColor, bool aHasBorderRadius, const RectCornerRadii& aInnerClipRadii, const Rect aSkipRect) { if ((aBlurRadius.width <= 0 && aBlurRadius.height <= 0)) { // The outer path must be rounded out // If not blurring, we're done now. Rect pathRect(aDestinationRect); pathRect.RoundOut(); FillDestinationPath(aDestinationCtx, pathRect, aShadowClipRect, aShadowColor, aHasBorderRadius, aInnerClipRadii); return; } DrawTarget* destDrawTarget = aDestinationCtx->GetDrawTarget(); Rect outerRect; Rect innerRect; Margin pathMargins; ComputeRectsForInsetBoxShadow(aBlurRadius, aSpreadRadius, aDestinationRect, aShadowClipRect, outerRect, innerRect, pathMargins); IntPoint topLeft; RefPtr<SourceSurface> minInsetBlur = GetInsetBlur(outerRect, innerRect, aBlurRadius, aSpreadRadius, aInnerClipRadii, aShadowColor, aHasBorderRadius, topLeft, aDestinationCtx); if (!minInsetBlur) { return; } Rect destRectOuter(aDestinationRect); destRectOuter.RoundIn(); Rect destRectInner(destRectOuter); destRectInner.Deflate(pathMargins); Rect srcRectOuter(outerRect); srcRectOuter.MoveBy(abs(topLeft.x), abs(topLeft.y)); Rect srcRectInner(srcRectOuter); srcRectInner.Deflate(pathMargins); if (srcRectOuter.IsEqualInterior(srcRectInner)) { destDrawTarget->DrawSurface(minInsetBlur, destRectOuter, srcRectOuter); } else { DrawBoxShadows(*destDrawTarget, minInsetBlur, destRectOuter, destRectInner, srcRectOuter, srcRectInner, aSkipRect); } }
static void DrawCorner(DrawTarget& aDT, SourceSurface* aSurface, const Rect& aDest, const Rect& aSrc, Rect& aSkipRect) { if (aSkipRect.Contains(aDest)) { return; } aDT.DrawSurface(aSurface, aDest, aSrc); }
/*** * Blur an inset box shadow by doing: * 1) Create a minimal box shadow path that creates a frame. * 2) Draw the box shadow portion over the destination surface. * 3) The "inset" part is created by a clip rect that properly clips * the alpha mask so that it has clean edges. We still create the full * proper alpha mask, but let the clip deal with the clean edges. * * All parameters should already be in device pixels. */ void gfxAlphaBoxBlur::BlurInsetBox(gfxContext* aDestinationCtx, const Rect aDestinationRect, const Rect aShadowClipRect, const IntSize aBlurRadius, const IntSize aSpreadRadius, const Color& aShadowColor, bool aHasBorderRadius, const RectCornerRadii& aInnerClipRadii, const Rect aSkipRect, const Point aShadowOffset) { DrawTarget* destDrawTarget = aDestinationCtx->GetDrawTarget(); // Blur inset shadows ALWAYS have a 0 spread radius. if ((aBlurRadius.width <= 0 && aBlurRadius.height <= 0)) { FillDestinationPath(aDestinationCtx, aDestinationRect, aShadowClipRect, aShadowColor, aHasBorderRadius, aInnerClipRadii); return; } IntMargin extendDest; IntMargin slice; bool didMoveOffset; RefPtr<SourceSurface> minInsetBlur = GetInsetBlur(extendDest, slice, aDestinationRect, aShadowClipRect, aBlurRadius, aSpreadRadius, aInnerClipRadii, aShadowColor, aHasBorderRadius, aShadowOffset, didMoveOffset, destDrawTarget); if (!minInsetBlur) { return; } Rect srcOuter(Point(), Size(minInsetBlur->GetSize())); Rect srcInner = srcOuter; srcInner.Deflate(Margin(slice)); Rect dstOuter(aDestinationRect); if (!didMoveOffset) { dstOuter.MoveBy(aShadowOffset); } dstOuter.Inflate(Margin(extendDest)); Rect dstInner = dstOuter; dstInner.Deflate(Margin(slice)); if (dstOuter.Size() == srcOuter.Size()) { destDrawTarget->DrawSurface(minInsetBlur, dstOuter, srcOuter); } else { DrawBoxShadows(*destDrawTarget, minInsetBlur, dstOuter, dstInner, srcOuter, srcInner, aSkipRect); } }
void ClientLayerManager::MakeSnapshotIfRequired() { if (!mShadowTarget) { return; } if (mWidget) { if (CompositorBridgeChild* remoteRenderer = GetRemoteRenderer()) { // The compositor doesn't draw to a different sized surface // when there's a rotation. Instead we rotate the result // when drawing into dt LayoutDeviceIntRect outerBounds; mWidget->GetBounds(outerBounds); IntRect bounds = ToOutsideIntRect(mShadowTarget->GetClipExtents()); if (mTargetRotation) { bounds = RotateRect(bounds, outerBounds.ToUnknownRect(), mTargetRotation); } SurfaceDescriptor inSnapshot; if (!bounds.IsEmpty() && mForwarder->AllocSurfaceDescriptor(bounds.Size(), gfxContentType::COLOR_ALPHA, &inSnapshot)) { // Make a copy of |inSnapshot| because the call to send it over IPC // will call forget() on the Shmem inside, and zero it out. SurfaceDescriptor outSnapshot = inSnapshot; if (remoteRenderer->SendMakeSnapshot(inSnapshot, bounds)) { RefPtr<DataSourceSurface> surf = GetSurfaceForDescriptor(outSnapshot); DrawTarget* dt = mShadowTarget->GetDrawTarget(); Rect dstRect(bounds.x, bounds.y, bounds.width, bounds.height); Rect srcRect(0, 0, bounds.width, bounds.height); gfx::Matrix rotate = ComputeTransformForUnRotation(outerBounds.ToUnknownRect(), mTargetRotation); gfx::Matrix oldMatrix = dt->GetTransform(); dt->SetTransform(rotate * oldMatrix); dt->DrawSurface(surf, dstRect, srcRect, DrawSurfaceOptions(), DrawOptions(1.0f, CompositionOp::OP_OVER)); dt->SetTransform(oldMatrix); } mForwarder->DestroySurfaceDescriptor(&outSnapshot); } } } mShadowTarget = nullptr; }
void ClientLayerManager::MakeSnapshotIfRequired() { if (!mShadowTarget) { return; } if (mWidget) { if (CompositorChild* remoteRenderer = GetRemoteRenderer()) { // The compositor doesn't draw to a different sized surface // when there's a rotation. Instead we rotate the result // when drawing into dt nsIntRect outerBounds; mWidget->GetBounds(outerBounds); nsIntRect bounds = ToOutsideIntRect(mShadowTarget->GetClipExtents()); if (mTargetRotation) { bounds = RotateRect(bounds, outerBounds, mTargetRotation); } SurfaceDescriptor inSnapshot; if (!bounds.IsEmpty() && mForwarder->AllocSurfaceDescriptor(bounds.Size().ToIntSize(), gfxContentType::COLOR_ALPHA, &inSnapshot) && remoteRenderer->SendMakeSnapshot(inSnapshot, bounds)) { RefPtr<DataSourceSurface> surf = GetSurfaceForDescriptor(inSnapshot); DrawTarget* dt = mShadowTarget->GetDrawTarget(); Rect dstRect(bounds.x, bounds.y, bounds.width, bounds.height); Rect srcRect(0, 0, bounds.width, bounds.height); gfx::Matrix rotate = ComputeTransformForUnRotation(outerBounds, mTargetRotation); gfx::Matrix oldMatrix = dt->GetTransform(); dt->SetTransform(oldMatrix * rotate); dt->DrawSurface(surf, dstRect, srcRect, DrawSurfaceOptions(), DrawOptions(1.0f, CompositionOp::OP_OVER)); dt->SetTransform(oldMatrix); } mForwarder->DestroySharedSurface(&inSnapshot); } } mShadowTarget = nullptr; }
void gfxQuartzNativeDrawing::EndNativeDrawing() { NS_ASSERTION(mCGContext, "EndNativeDrawing called without BeginNativeDrawing"); if (mBorrowedContext.cg) { MOZ_ASSERT(!mContext->IsCairo()); mBorrowedContext.Finish(); if (mDrawTarget) { DrawTarget *dest = mContext->GetDrawTarget(); RefPtr<SourceSurface> source = mDrawTarget->Snapshot(); IntSize backingSize(NSToIntFloor(mNativeRect.width * mBackingScale), NSToIntFloor(mNativeRect.height * mBackingScale)); Matrix oldTransform = dest->GetTransform(); Matrix newTransform = oldTransform; newTransform.Translate(mNativeRect.x, mNativeRect.y); newTransform.Scale(1.0f / mBackingScale, 1.0f / mBackingScale); dest->SetTransform(newTransform); dest->DrawSurface(source, gfx::Rect(0, 0, backingSize.width, backingSize.height), gfx::Rect(0, 0, backingSize.width, backingSize.height)); dest->SetTransform(oldTransform); } return; } cairo_quartz_finish_cg_context_with_clip(mSurfaceContext->GetCairo()); mQuartzSurface->MarkDirty(); if (mSurfaceContext != mContext) { gfxContextMatrixAutoSaveRestore save(mContext); // Copy back to destination mContext->Translate(mNativeRect.TopLeft()); mContext->Scale(1.0f / mBackingScale, 1.0f / mBackingScale); mContext->DrawSurface(mQuartzSurface, mQuartzSurface->GetSize()); } }
void gfxXlibNativeRenderer::Draw(gfxContext* ctx, nsIntSize size, uint32_t flags, Screen *screen, Visual *visual) { gfxMatrix matrix = ctx->CurrentMatrix(); // We can only draw direct or onto a copied background if pixels align and // native drawing is compatible with the current operator. (The matrix is // actually also pixel-exact for flips and right-angle rotations, which // would permit copying the background but not drawing direct.) bool matrixIsIntegerTranslation = !matrix.HasNonIntegerTranslation(); bool canDrawOverBackground = matrixIsIntegerTranslation && ctx->CurrentOperator() == gfxContext::OPERATOR_OVER; // The padding of 0.5 for non-pixel-exact transformations used here is // the same as what _cairo_pattern_analyze_filter uses. const gfxFloat filterRadius = 0.5; gfxRect affectedRect(0.0, 0.0, size.width, size.height); if (!matrixIsIntegerTranslation) { // The filter footprint means that the affected rectangle is a // little larger than the drawingRect; affectedRect.Inflate(filterRadius); NATIVE_DRAWING_NOTE("FALLBACK: matrix not integer translation"); } else if (!canDrawOverBackground) { NATIVE_DRAWING_NOTE("FALLBACK: unsupported operator"); } // Clipping to the region affected by drawing allows us to consider only // the portions of the clip region that will be affected by drawing. gfxRect clipExtents; { gfxContextAutoSaveRestore autoSR(ctx); ctx->Clip(affectedRect); clipExtents = ctx->GetClipExtents(); if (clipExtents.IsEmpty()) return; // nothing to do if (canDrawOverBackground && DrawDirect(ctx, size, flags, screen, visual)) return; } IntRect drawingRect(IntPoint(0, 0), size); // Drawing need only be performed within the clip extents // (and padding for the filter). if (!matrixIsIntegerTranslation) { // The source surface may need to be a little larger than the clip // extents due to the filter footprint. clipExtents.Inflate(filterRadius); } clipExtents.RoundOut(); IntRect intExtents(int32_t(clipExtents.X()), int32_t(clipExtents.Y()), int32_t(clipExtents.Width()), int32_t(clipExtents.Height())); drawingRect.IntersectRect(drawingRect, intExtents); gfxPoint offset(drawingRect.x, drawingRect.y); DrawingMethod method; DrawTarget* drawTarget = ctx->GetDrawTarget(); Matrix dtTransform = drawTarget->GetTransform(); gfxPoint deviceTranslation = gfxPoint(dtTransform._31, dtTransform._32); cairo_surface_t* cairoTarget = static_cast<cairo_surface_t*> (drawTarget->GetNativeSurface(NativeSurfaceType::CAIRO_SURFACE)); cairo_surface_t* tempXlibSurface = CreateTempXlibSurface(cairoTarget, drawTarget, size, canDrawOverBackground, flags, screen, visual, &method); if (!tempXlibSurface) return; bool drawIsOpaque = (flags & DRAW_IS_OPAQUE) != 0; if (!drawIsOpaque) { cairo_t* tmpCtx = cairo_create(tempXlibSurface); if (method == eCopyBackground) { NS_ASSERTION(cairoTarget, "eCopyBackground only used when there's a cairoTarget"); cairo_set_operator(tmpCtx, CAIRO_OPERATOR_SOURCE); gfxPoint pt = -(offset + deviceTranslation); cairo_set_source_surface(tmpCtx, cairoTarget, pt.x, pt.y); // The copy from the tempXlibSurface to the target context should // use operator SOURCE, but that would need a mask to bound the // operation. Here we only copy opaque backgrounds so operator // OVER will behave like SOURCE masked by the surface. NS_ASSERTION(cairo_surface_get_content(tempXlibSurface) == CAIRO_CONTENT_COLOR, "Don't copy background with a transparent surface"); } else { cairo_set_operator(tmpCtx, CAIRO_OPERATOR_CLEAR); } cairo_paint(tmpCtx); cairo_destroy(tmpCtx); } if (!DrawOntoTempSurface(tempXlibSurface, -drawingRect.TopLeft())) { cairo_surface_destroy(tempXlibSurface); return; } SurfaceFormat moz2DFormat = cairo_surface_get_content(tempXlibSurface) == CAIRO_CONTENT_COLOR ? SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8; if (method != eAlphaExtraction) { if (drawTarget) { NativeSurface native; native.mFormat = moz2DFormat; native.mType = NativeSurfaceType::CAIRO_SURFACE; native.mSurface = tempXlibSurface; native.mSize = size; RefPtr<SourceSurface> sourceSurface = drawTarget->CreateSourceSurfaceFromNativeSurface(native); if (sourceSurface) { drawTarget->DrawSurface(sourceSurface, Rect(offset.x, offset.y, size.width, size.height), Rect(0, 0, size.width, size.height)); } } else { nsRefPtr<gfxASurface> tmpSurf = gfxASurface::Wrap(tempXlibSurface); ctx->SetSource(tmpSurf, offset); ctx->Paint(); } cairo_surface_destroy(tempXlibSurface); return; } nsRefPtr<gfxImageSurface> blackImage = CopyXlibSurfaceToImage(tempXlibSurface, size, gfxImageFormat::ARGB32); cairo_t* tmpCtx = cairo_create(tempXlibSurface); cairo_set_source_rgba(tmpCtx, 1.0, 1.0, 1.0, 1.0); cairo_set_operator(tmpCtx, CAIRO_OPERATOR_SOURCE); cairo_paint(tmpCtx); cairo_destroy(tmpCtx); DrawOntoTempSurface(tempXlibSurface, -drawingRect.TopLeft()); nsRefPtr<gfxImageSurface> whiteImage = CopyXlibSurfaceToImage(tempXlibSurface, size, gfxImageFormat::RGB24); if (blackImage->CairoStatus() == CAIRO_STATUS_SUCCESS && whiteImage->CairoStatus() == CAIRO_STATUS_SUCCESS) { if (!gfxAlphaRecovery::RecoverAlpha(blackImage, whiteImage)) { cairo_surface_destroy(tempXlibSurface); return; } gfxASurface* paintSurface = blackImage; if (drawTarget) { NativeSurface native; native.mFormat = moz2DFormat; native.mType = NativeSurfaceType::CAIRO_SURFACE; native.mSurface = paintSurface->CairoSurface(); native.mSize = size; RefPtr<SourceSurface> sourceSurface = drawTarget->CreateSourceSurfaceFromNativeSurface(native); if (sourceSurface) { drawTarget->DrawSurface(sourceSurface, Rect(offset.x, offset.y, size.width, size.height), Rect(0, 0, size.width, size.height)); } } else { ctx->SetSource(paintSurface, offset); ctx->Paint(); } } cairo_surface_destroy(tempXlibSurface); }
/* static */ void gfxAlphaBoxBlur::BlurRectangle(gfxContext* aDestinationCtx, const gfxRect& aRect, const RectCornerRadii* aCornerRadii, const gfxPoint& aBlurStdDev, const Color& aShadowColor, const gfxRect& aDirtyRect, const gfxRect& aSkipRect) { IntSize blurRadius = CalculateBlurRadius(aBlurStdDev); bool mirrorCorners = !aCornerRadii || aCornerRadii->AreRadiiSame(); IntRect rect = RoundedToInt(ToRect(aRect)); IntMargin blurMargin; IntMargin slice; IntSize minSize; RefPtr<SourceSurface> boxShadow = GetBlur(aDestinationCtx, rect.Size(), blurRadius, aCornerRadii, aShadowColor, mirrorCorners, blurMargin, slice, minSize); if (!boxShadow) { return; } DrawTarget* destDrawTarget = aDestinationCtx->GetDrawTarget(); destDrawTarget->PushClipRect(ToRect(aDirtyRect)); // Copy the right parts from boxShadow into destDrawTarget. The middle parts // will be stretched, border-image style. Rect srcOuter(Point(blurMargin.left, blurMargin.top), Size(minSize)); Rect srcInner(srcOuter); srcOuter.Inflate(Margin(blurMargin)); srcInner.Deflate(Margin(slice)); Rect dstOuter(rect); Rect dstInner(rect); dstOuter.Inflate(Margin(blurMargin)); dstInner.Deflate(Margin(slice)); Rect skipRect = ToRect(aSkipRect); if (minSize == rect.Size()) { // The target rect is smaller than the minimal size so just draw the surface if (mirrorCorners) { DrawMirroredBoxShadow(destDrawTarget, boxShadow, dstOuter); } else { destDrawTarget->DrawSurface(boxShadow, dstOuter, srcOuter); } } else { if (mirrorCorners) { DrawMirroredMinBoxShadow(destDrawTarget, boxShadow, dstOuter, dstInner, srcOuter, srcInner, skipRect, true); } else { DrawMinBoxShadow(destDrawTarget, boxShadow, dstOuter, dstInner, srcOuter, srcInner, skipRect, true); } } // 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(); }
void gfxAlphaBoxBlur::BlurInsetBox(gfxContext* aDestinationCtx, const Rect& aDestinationRect, const Rect& aShadowClipRect, const IntSize& aBlurRadius, const Color& aShadowColor, const RectCornerRadii* aInnerClipRadii, const Rect& aSkipRect, const Point& aShadowOffset) { if ((aBlurRadius.width == 0 && aBlurRadius.height == 0) || aShadowClipRect.IsEmpty()) { FillDestinationPath(aDestinationCtx, aDestinationRect, aShadowClipRect, aShadowColor, aInnerClipRadii); return; } DrawTarget* destDrawTarget = aDestinationCtx->GetDrawTarget(); Margin innerMargin; Margin blurMargin; GetBlurMargins(aInnerClipRadii, aBlurRadius, blurMargin, innerMargin); Rect whitespaceRect; Rect outerRect; bool useDestRect = GetInsetBoxShadowRects(blurMargin, innerMargin, aShadowClipRect, aDestinationRect, whitespaceRect, outerRect); // Check that the inset margin between the outer and whitespace rects is symmetric, // and that all corner radii are the same, in which case the blur can be mirrored. Margin checkMargin = outerRect - whitespaceRect; bool mirrorCorners = checkMargin.left == checkMargin.right && checkMargin.top == checkMargin.bottom && (!aInnerClipRadii || aInnerClipRadii->AreRadiiSame()); RefPtr<SourceSurface> minBlur = GetInsetBlur(outerRect, whitespaceRect, useDestRect, aShadowColor, aBlurRadius, aInnerClipRadii, destDrawTarget, mirrorCorners); if (!minBlur) { return; } if (useDestRect) { Rect destBlur = aDestinationRect; destBlur.Inflate(blurMargin); if (mirrorCorners) { DrawMirroredBoxShadow(destDrawTarget, minBlur.get(), destBlur); } else { Rect srcBlur(Point(0, 0), Size(minBlur->GetSize())); MOZ_ASSERT(srcBlur.Size() == destBlur.Size()); destDrawTarget->DrawSurface(minBlur, destBlur, srcBlur); } } else { Rect srcOuter(outerRect); Rect srcInner(srcOuter); srcInner.Deflate(blurMargin); // The outer color fill srcInner.Deflate(innerMargin); // The inner whitespace // The shadow clip rect already takes into account the spread radius Rect outerFillRect(aShadowClipRect); outerFillRect.Inflate(blurMargin); FillDestinationPath(aDestinationCtx, aDestinationRect, outerFillRect, aShadowColor); // Inflate once for the frame around the whitespace Rect destRect(aShadowClipRect); destRect.Inflate(blurMargin); // Deflate for the blurred in white space Rect destInnerRect(aShadowClipRect); destInnerRect.Deflate(innerMargin); if (mirrorCorners) { DrawMirroredMinBoxShadow(destDrawTarget, minBlur, destRect, destInnerRect, srcOuter, srcInner, aSkipRect); } else { DrawMinBoxShadow(destDrawTarget, minBlur, destRect, destInnerRect, srcOuter, srcInner, aSkipRect); } } }