Пример #1
0
static already_AddRefed<SourceSurface>
GetBlur(gfxContext* aDestinationCtx,
        const IntSize& aRectSize,
        const IntSize& aBlurRadius,
        RectCornerRadii* aCornerRadii,
        const Color& aShadowColor,
        IntMargin& aExtendDestBy,
        IntMargin& aSlice)
{
  if (!gBlurCache) {
    gBlurCache = new BlurCache();
  }

  IntSize minSize =
    ComputeMinSizeForShadowShape(aCornerRadii, aBlurRadius, aSlice, aRectSize);

  // We can get seams using the min size rect when drawing to the destination rect
  // if we have a non-pixel aligned destination transformation. In those cases,
  // fallback to just rendering the destination rect.
  Matrix destMatrix = ToMatrix(aDestinationCtx->CurrentMatrix());
  bool useDestRect = !destMatrix.IsRectilinear() || destMatrix.HasNonIntegerTranslation();
  if (useDestRect) {
    minSize = aRectSize;
  }

  DrawTarget& destDT = *aDestinationCtx->GetDrawTarget();

  BlurCacheData* cached = gBlurCache->Lookup(minSize, aBlurRadius,
                                             aCornerRadii, aShadowColor,
                                             destDT.GetBackendType());
  if (cached && !useDestRect) {
    // See CreateBlurMask() for these values
    aExtendDestBy = cached->mExtendDest;
    aSlice = aSlice + aExtendDestBy;
    RefPtr<SourceSurface> blur = cached->mBlur;
    return blur.forget();
  }

  RefPtr<SourceSurface> blurMask =
    CreateBlurMask(minSize, aCornerRadii, aBlurRadius, aExtendDestBy, aSlice,
                   destDT);

  if (!blurMask) {
    return nullptr;
  }

  RefPtr<SourceSurface> boxShadow = CreateBoxShadow(blurMask, aShadowColor);
  if (!boxShadow) {
    return nullptr;
  }

  if (useDestRect) {
    // Since we're just going to paint the actual rect to the destination
    aSlice.SizeTo(0, 0, 0, 0);
  } else {
    CacheBlur(destDT, minSize, aBlurRadius, aCornerRadii, aShadowColor,
              aExtendDestBy, boxShadow);
  }
  return boxShadow.forget();
}
Пример #2
0
// Blurs a small surface and creates the mask.
static already_AddRefed<SourceSurface>
CreateBlurMask(const IntSize& aRectSize,
               RectCornerRadii* aCornerRadii,
               gfxIntSize aBlurRadius,
               IntMargin& aExtendDestBy,
               IntMargin& aSliceBorder,
               DrawTarget& aDestDrawTarget)
{
  IntMargin slice;
  gfxAlphaBoxBlur blur;
  IntSize minSize =
    ComputeMinSizeForShadowShape(aCornerRadii, aBlurRadius, slice, aRectSize);
  IntRect minRect(IntPoint(), minSize);

  gfxContext* blurCtx = blur.Init(ThebesRect(Rect(minRect)), gfxIntSize(),
                                  aBlurRadius, nullptr, nullptr);

  if (!blurCtx) {
    return nullptr;
  }

  DrawTarget* blurDT = blurCtx->GetDrawTarget();
  ColorPattern black(Color(0.f, 0.f, 0.f, 1.f));

  if (aCornerRadii) {
    RefPtr<Path> roundedRect =
      MakePathForRoundedRect(*blurDT, Rect(minRect), *aCornerRadii);
    blurDT->Fill(roundedRect, black);
  } else {
    blurDT->FillRect(Rect(minRect), black);
  }

  IntPoint topLeft;
  RefPtr<SourceSurface> result = blur.DoBlur(&aDestDrawTarget, &topLeft);
  if (!result) {
    return nullptr;
  }

  IntRect expandedMinRect(topLeft, result->GetSize());
  aExtendDestBy = expandedMinRect - minRect;
  aSliceBorder = slice + aExtendDestBy;

  MOZ_ASSERT(aSliceBorder.LeftRight() <= expandedMinRect.width);
  MOZ_ASSERT(aSliceBorder.TopBottom() <= expandedMinRect.height);

  return result.forget();
}
Пример #3
0
static IntSize
ComputeMinSizeForShadowShape(RectCornerRadii* aCornerRadii,
                             gfxIntSize aBlurRadius,
                             IntMargin& aSlice,
                             const IntSize& aRectSize)
{
  float cornerWidth = 0;
  float cornerHeight = 0;
  if (aCornerRadii) {
    RectCornerRadii corners = *aCornerRadii;
    for (size_t i = 0; i < 4; i++) {
      cornerWidth = std::max(cornerWidth, corners[i].width);
      cornerHeight = std::max(cornerHeight, corners[i].height);
    }
  }

  aSlice = IntMargin(ceil(cornerHeight) + aBlurRadius.height,
                     ceil(cornerWidth) + aBlurRadius.width,
                     ceil(cornerHeight) + aBlurRadius.height,
                     ceil(cornerWidth) + aBlurRadius.width);

  IntSize minSize(aSlice.LeftRight() + 1,
                      aSlice.TopBottom() + 1);

  // If aRectSize is smaller than minSize, the border-image approach won't
  // work; there's no way to squeeze parts of the min box-shadow source
  // image such that the result looks correct. So we need to adjust minSize
  // in such a way that we can later draw it without stretching in the affected
  // dimension. We also need to adjust "slice" to ensure that we're not trying
  // to slice away more than we have.
  if (aRectSize.width < minSize.width) {
    minSize.width = aRectSize.width;
    aSlice.left = 0;
    aSlice.right = 0;
  }
  if (aRectSize.height < minSize.height) {
    minSize.height = aRectSize.height;
    aSlice.top = 0;
    aSlice.bottom = 0;
  }

  MOZ_ASSERT(aSlice.LeftRight() <= minSize.width);
  MOZ_ASSERT(aSlice.TopBottom() <= minSize.height);
  return minSize;
}
Пример #4
0
static IntSize
ComputeMinSizeForShadowShape(const RectCornerRadii* aCornerRadii,
                             const IntSize& aBlurRadius,
                             IntMargin& aOutSlice,
                             const IntSize& aRectSize)
{
  Size cornerSize(0, 0);
  if (aCornerRadii) {
    const RectCornerRadii& corners = *aCornerRadii;
    NS_FOR_CSS_FULL_CORNERS(i) {
      cornerSize.width = std::max(cornerSize.width, corners[i].width);
      cornerSize.height = std::max(cornerSize.height, corners[i].height);
    }
  }

  IntSize margin = IntSize::Ceil(cornerSize) + aBlurRadius;
  aOutSlice = IntMargin(margin.height, margin.width,
                        margin.height, margin.width);

  IntSize minSize(aOutSlice.LeftRight() + 1,
                  aOutSlice.TopBottom() + 1);

  // If aRectSize is smaller than minSize, the border-image approach won't
  // work; there's no way to squeeze parts of the min box-shadow source
  // image such that the result looks correct. So we need to adjust minSize
  // in such a way that we can later draw it without stretching in the affected
  // dimension. We also need to adjust "slice" to ensure that we're not trying
  // to slice away more than we have.
  if (aRectSize.width < minSize.width) {
    minSize.width = aRectSize.width;
    aOutSlice.left = 0;
    aOutSlice.right = 0;
  }
  if (aRectSize.height < minSize.height) {
    minSize.height = aRectSize.height;
    aOutSlice.top = 0;
    aOutSlice.bottom = 0;
  }

  MOZ_ASSERT(aOutSlice.LeftRight() <= minSize.width);
  MOZ_ASSERT(aOutSlice.TopBottom() <= minSize.height);
  return minSize;
}
Пример #5
0
already_AddRefed<mozilla::gfx::SourceSurface>
gfxAlphaBoxBlur::GetInsetBlur(IntMargin& aExtendDestBy,
                              IntMargin& aSlice,
                              const Rect aDestinationRect,
                              const Rect aShadowClipRect,
                              const IntSize& aBlurRadius,
                              const IntSize& aSpreadRadius,
                              const RectCornerRadii& aInnerClipRadii,
                              const Color& aShadowColor,
                              const bool& aHasBorderRadius,
                              const Point aShadowOffset,
                              bool& aMovedOffset,
                              DrawTarget* aDestDrawTarget)
{
  if (!gBlurCache) {
    gBlurCache = new BlurCache();
  }

  IntRect outerRect;
  IntRect innerRect;
  ComputeRectsForInsetBoxShadow(aBlurRadius, aSpreadRadius,
                                outerRect, innerRect,
                                aSlice, aDestinationRect,
                                aShadowClipRect, aHasBorderRadius,
                                aInnerClipRadii);

  // If we have a shadow offset larger than the min rect,
  // there's no clean way we can properly create a min rect with the offset
  // in the correct place and still render correctly.  In those cases,
  // fallback to just rendering the dest rect as is.
  bool useDestRect = (std::abs(aShadowOffset.x) > aSlice.left) ||
                     (std::abs(aShadowOffset.y) > aSlice.top);
  aMovedOffset = false;
  if (useDestRect) {
    aDestinationRect.ToIntRect(&outerRect);
    aShadowClipRect.ToIntRect(&innerRect);
    aMovedOffset = true;
  }

  BlurCacheData* cached =
      gBlurCache->LookupInsetBoxShadow(outerRect.Size(), innerRect.Size(),
                                       aBlurRadius, aSpreadRadius,
                                       &aInnerClipRadii, aShadowColor,
                                       aHasBorderRadius,
                                       aDestDrawTarget->GetBackendType());
  if (cached && !useDestRect) {
    aExtendDestBy = cached->mExtendDest;
    // Need to extend it twice: once for the outer rect and once for the inner rect.
    aSlice += aExtendDestBy;
    aSlice += aExtendDestBy;

    // So we don't forget the actual cached blur
    RefPtr<SourceSurface> cachedBlur = cached->mBlur;
    return cachedBlur.forget();
  }

  // Dirty rect and skip rect are null for the min inset shadow.
  // When rendering inset box shadows, we respect the spread radius by changing
  //  the shape of the unblurred shadow, and can pass a spread radius of zero here.
  IntSize zeroSpread(0, 0);
  gfxContext* minGfxContext = Init(ThebesRect(outerRect),
                                   zeroSpread, aBlurRadius, nullptr, nullptr);
  if (!minGfxContext) {
    return nullptr;
  }

  DrawTarget* minDrawTarget = minGfxContext->GetDrawTarget();
  RefPtr<Path> maskPath =
    GetBoxShadowInsetPath(minDrawTarget, IntRectToRect(outerRect),
                          IntRectToRect(innerRect), aHasBorderRadius,
                          aInnerClipRadii);

  Color black(0.f, 0.f, 0.f, 1.f);
  minGfxContext->SetColor(black);
  minGfxContext->SetPath(maskPath);
  minGfxContext->Fill();

  IntPoint topLeft;
  RefPtr<SourceSurface> minMask = DoBlur(minDrawTarget, &topLeft);
  if (!minMask) {
    return nullptr;
  }

  RefPtr<SourceSurface> minInsetBlur = CreateBoxShadow(minMask, aShadowColor);
  if (!minInsetBlur) {
    return nullptr;
  }

  IntRect blurRect(topLeft, minInsetBlur->GetSize());
  aExtendDestBy = blurRect - outerRect;

  if (useDestRect) {
    // Since we're just going to paint the actual rect to the destination
    aSlice.SizeTo(0, 0, 0, 0);
  } else {
    aSlice += aExtendDestBy;
    aSlice += aExtendDestBy;

    CacheInsetBlur(outerRect.Size(), innerRect.Size(),
                 aBlurRadius, aSpreadRadius,
                 &aInnerClipRadii, aShadowColor,
                 aHasBorderRadius, aDestDrawTarget->GetBackendType(),
                 aExtendDestBy, minInsetBlur);

  }

  return minInsetBlur.forget();
}
Пример #6
0
static void
ComputeRectsForInsetBoxShadow(IntSize aBlurRadius,
                              IntSize aSpreadRadius,
                              IntRect& aOutOuterRect,
                              IntRect& aOutInnerRect,
                              IntMargin& aOutPathMargins,
                              const Rect& aDestRect,
                              const Rect& aShadowClipRect,
                              bool aHasBorderRadius,
                              const RectCornerRadii& aInnerClipRectRadii)
{
  IntSize rectBufferSize = aBlurRadius + aSpreadRadius;
  float cornerWidth = 0;
  float cornerHeight = 0;
  if (aHasBorderRadius) {
    for (size_t i = 0; i < 4; i++) {
      cornerWidth = std::max(cornerWidth, aInnerClipRectRadii[i].width);
      cornerHeight = std::max(cornerHeight, aInnerClipRectRadii[i].height);
    }
  }

  // Create the inner rect to be the smallest possible size based on
  // blur / spread / corner radii
  IntMargin innerMargin = IntMargin(ceil(cornerHeight) + rectBufferSize.height,
                                    ceil(cornerWidth) + rectBufferSize.width,
                                    ceil(cornerHeight) + rectBufferSize.height,
                                    ceil(cornerWidth) + rectBufferSize.width);
  aOutPathMargins = innerMargin;

  // If we have a negative spread radius, we would not have enough
  // size to actually do the blur. So the min size must be the abs() of the blur
  // and spread radius.
  IntSize minBlurSize(std::abs(aSpreadRadius.width) + std::abs(aBlurRadius.width),
                      std::abs(aSpreadRadius.height) + std::abs(aBlurRadius.height));

  IntMargin minInnerMargins = IntMargin(ceil(cornerHeight) + minBlurSize.height,
                                        ceil(cornerWidth) + minBlurSize.width,
                                        ceil(cornerHeight) + minBlurSize.height,
                                        ceil(cornerWidth) + minBlurSize.width);

  IntSize minInnerSize(minInnerMargins.LeftRight() + 1,
                       minInnerMargins.TopBottom() + 1);

  if (aShadowClipRect.height < minInnerSize.height) {
    minInnerSize.height = aShadowClipRect.height;
  }

  if (aShadowClipRect.width < minInnerSize.width) {
    minInnerSize.width = aShadowClipRect.width;
  }

  // Then expand the outer rect based on the size between the inner/outer rects
  IntSize minOuterSize(minInnerSize);
  IntMargin outerRectMargin(rectBufferSize.height, rectBufferSize.width,
                            rectBufferSize.height, rectBufferSize.width);
  minOuterSize.width += outerRectMargin.LeftRight();
  minOuterSize.height += outerRectMargin.TopBottom();

  aOutOuterRect = IntRect(IntPoint(), minOuterSize);
  aOutInnerRect = IntRect(IntPoint(rectBufferSize.width, rectBufferSize.height), minInnerSize);

  if (aShadowClipRect.IsEmpty()) {
    aOutInnerRect.width = 0;
    aOutInnerRect.height = 0;
  }
}