Example #1
0
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);
}
Example #2
0
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);
  }
}
Example #5
0
/***
 * 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);
 }
}
Example #6
0
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);
}
Example #7
0
/***
 * 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);
}
Example #12
0
/* 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();
}
Example #13
0
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);
    }
  }
}