Example #1
0
DrawResult
nsSVGClipPathFrame::PaintClipMask(gfxContext& aMaskContext,
                                  nsIFrame* aClippedFrame,
                                  const gfxMatrix& aMatrix,
                                  Matrix* aMaskTransform,
                                  SourceSurface* aExtraMask,
                                  const Matrix& aExtraMasksTransform)
{
    // A clipPath can reference another clipPath.  We re-enter this method for
    // each clipPath in a reference chain, so here we limit chain length:
    static int16_t sRefChainLengthCounter = AutoReferenceLimiter::notReferencing;
    AutoReferenceLimiter
    refChainLengthLimiter(&sRefChainLengthCounter,
                          MAX_SVG_CLIP_PATH_REFERENCE_CHAIN_LENGTH);
    if (!refChainLengthLimiter.Reference()) {
        return DrawResult::SUCCESS; // Reference chain is too long!
    }

    // And to prevent reference loops we check that this clipPath only appears
    // once in the reference chain (if any) that we're currently processing:
    AutoReferenceLimiter refLoopDetector(&mReferencing, 1);
    if (!refLoopDetector.Reference()) {
        return DrawResult::SUCCESS; // Reference loop!
    }

    DrawResult result = DrawResult::SUCCESS;
    DrawTarget* maskDT = aMaskContext.GetDrawTarget();
    MOZ_ASSERT(maskDT->GetFormat() == SurfaceFormat::A8);

    // Paint this clipPath's contents into aMaskDT:
    // We need to set mMatrixForChildren here so that under the PaintSVG calls
    // on our children (below) our GetCanvasTM() method will return the correct
    // transform.
    mMatrixForChildren = GetClipPathTransform(aClippedFrame) * aMatrix;

    // Check if this clipPath is itself clipped by another clipPath:
    nsSVGClipPathFrame* clipPathThatClipsClipPath =
        nsSVGEffects::GetEffectProperties(this).GetClipPathFrame();
    nsSVGUtils::MaskUsage maskUsage;
    nsSVGUtils::DetermineMaskUsage(this, true, maskUsage);

    if (maskUsage.shouldApplyClipPath) {
        clipPathThatClipsClipPath->ApplyClipPath(aMaskContext, aClippedFrame,
                aMatrix);
    } else if (maskUsage.shouldGenerateClipMaskLayer) {
        Matrix maskTransform;
        RefPtr<SourceSurface> maskSurface;
        Tie(result, maskSurface) =
            clipPathThatClipsClipPath->GetClipMask(aMaskContext, aClippedFrame,
                    aMatrix, &maskTransform);
        aMaskContext.PushGroupForBlendBack(gfxContentType::ALPHA, 1.0,
                                           maskSurface, maskTransform);
        // The corresponding PopGroupAndBlend call below will mask the
        // blend using |maskSurface|.
    }

    // Paint our children into the mask:
    for (nsIFrame* kid = mFrames.FirstChild(); kid;
            kid = kid->GetNextSibling()) {
        result &= PaintFrameIntoMask(kid, aClippedFrame, aMaskContext, aMatrix);
    }

    if (maskUsage.shouldGenerateClipMaskLayer) {
        aMaskContext.PopGroupAndBlend();
    } else if (maskUsage.shouldApplyClipPath) {
        aMaskContext.PopClip();
    }

    // Moz2D transforms in the opposite direction to Thebes
    gfxMatrix maskTransfrom = aMaskContext.CurrentMatrix();
    maskTransfrom.Invert();

    if (aExtraMask) {
        ComposeExtraMask(maskDT, maskTransfrom, aExtraMask, aExtraMasksTransform);
    }

    *aMaskTransform = ToMatrix(maskTransfrom);
    return result;
}
Example #2
0
DrawResult
nsSVGClipPathFrame::PaintFrameIntoMask(nsIFrame *aFrame,
                                       nsIFrame* aClippedFrame,
                                       gfxContext& aTarget,
                                       const gfxMatrix& aMatrix)
{
    nsISVGChildFrame* frame = do_QueryFrame(aFrame);
    if (!frame) {
        return DrawResult::SUCCESS;
    }

    // The CTM of each frame referencing us can be different.
    frame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED);

    // Children of this clipPath may themselves be clipped.
    nsSVGEffects::EffectProperties effectProperties =
        nsSVGEffects::GetEffectProperties(aFrame);
    if (effectProperties.HasInvalidClipPath()) {
        return DrawResult::SUCCESS;
    }
    nsSVGClipPathFrame *clipPathThatClipsChild =
        effectProperties.GetClipPathFrame();

    nsSVGUtils::MaskUsage maskUsage;
    nsSVGUtils::DetermineMaskUsage(aFrame, true, maskUsage);
    DrawResult result = DrawResult::SUCCESS;
    if (maskUsage.shouldApplyClipPath) {
        clipPathThatClipsChild->ApplyClipPath(aTarget, aClippedFrame, aMatrix);
    } else if (maskUsage.shouldGenerateClipMaskLayer) {
        Matrix maskTransform;
        RefPtr<SourceSurface> maskSurface;
        Tie(result, maskSurface) =
            clipPathThatClipsChild->GetClipMask(aTarget, aClippedFrame,
                                                aMatrix, &maskTransform);
        aTarget.PushGroupForBlendBack(gfxContentType::ALPHA, 1.0,
                                      maskSurface, maskTransform);
        // The corresponding PopGroupAndBlend call below will mask the
        // blend using |maskSurface|.
    }

    gfxMatrix toChildsUserSpace = mMatrixForChildren;
    nsIFrame* child = do_QueryFrame(frame);
    nsIContent* childContent = child->GetContent();
    if (childContent->IsSVGElement()) {
        toChildsUserSpace =
            static_cast<const nsSVGElement*>(childContent)->
            PrependLocalTransformsTo(mMatrixForChildren, eUserSpaceToParent);
    }

    // Our children have NS_STATE_SVG_CLIPPATH_CHILD set on them, and
    // SVGGeometryFrame::Render checks for that state bit and paints
    // only the geometry (opaque black) if set.
    result &= frame->PaintSVG(aTarget, toChildsUserSpace);

    if (maskUsage.shouldGenerateClipMaskLayer) {
        aTarget.PopGroupAndBlend();
    } else if (maskUsage.shouldApplyClipPath) {
        aTarget.PopClip();
    }

    return result;
}
Example #3
0
//----------------------------------------------------------------------
// nsISVGChildFrame methods:
nsresult
nsSVGImageFrame::PaintSVG(gfxContext& aContext,
                          const gfxMatrix& aTransform,
                          const nsIntRect *aDirtyRect)
{
  nsresult rv = NS_OK;

  if (!StyleVisibility()->IsVisible())
    return NS_OK;

  float x, y, width, height;
  SVGImageElement *imgElem = static_cast<SVGImageElement*>(mContent);
  imgElem->GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
  NS_ASSERTION(width > 0 && height > 0,
               "Should only be painting things with valid width/height");

  if (!mImageContainer) {
    nsCOMPtr<imgIRequest> currentRequest;
    nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
    if (imageLoader)
      imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
                              getter_AddRefs(currentRequest));

    if (currentRequest)
      currentRequest->GetImage(getter_AddRefs(mImageContainer));
  }

  if (mImageContainer) {
    gfxContextAutoSaveRestore autoRestorer(&aContext);

    if (StyleDisplay()->IsScrollableOverflow()) {
      gfxRect clipRect = nsSVGUtils::GetClipRectForFrame(this, x, y,
                                                         width, height);
      nsSVGUtils::SetClipRect(&aContext, aTransform, clipRect);
    }

    if (!TransformContextForPainting(&aContext, aTransform)) {
      return NS_ERROR_FAILURE;
    }

    // fill-opacity doesn't affect <image>, so if we're allowed to
    // optimize group opacity, the opacity used for compositing the
    // image into the current canvas is just the group opacity.
    float opacity = 1.0f;
    if (nsSVGUtils::CanOptimizeOpacity(this)) {
      opacity = StyleEffects()->mOpacity;
    }

    if (opacity != 1.0f || StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
      aContext.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity);
    }

    nscoord appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel();
    nsRect dirtyRect; // only used if aDirtyRect is non-null
    if (aDirtyRect) {
      NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
                   (mState & NS_FRAME_IS_NONDISPLAY),
                   "Display lists handle dirty rect intersection test");
      dirtyRect = ToAppUnits(*aDirtyRect, appUnitsPerDevPx);
      // Adjust dirtyRect to match our local coordinate system.
      nsRect rootRect =
        nsSVGUtils::TransformFrameRectToOuterSVG(mRect, aTransform,
                                                 PresContext());
      dirtyRect.MoveBy(-rootRect.TopLeft());
    }

    // XXXbholley - I don't think huge images in SVGs are common enough to
    // warrant worrying about the responsiveness impact of doing synchronous
    // decodes. The extra code complexity of determinining when we want to
    // force sync probably just isn't worth it, so always pass FLAG_SYNC_DECODE
    uint32_t drawFlags = imgIContainer::FLAG_SYNC_DECODE;

    if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) {
      // Package up the attributes of this image element which can override the
      // attributes of mImageContainer's internal SVG document.  The 'width' &
      // 'height' values we're passing in here are in CSS units (though they
      // come from width/height *attributes* in SVG). They influence the region
      // of the SVG image's internal document that is visible, in combination
      // with preserveAspectRatio and viewBox.
      SVGImageContext context(CSSIntSize(width, height),
                              Some(imgElem->mPreserveAspectRatio.GetAnimValue()));

      // For the actual draw operation to draw crisply (and at the right size),
      // our destination rect needs to be |width|x|height|, *in dev pixels*.
      LayoutDeviceSize devPxSize(width, height);
      nsRect destRect(nsPoint(),
                      LayoutDevicePixel::ToAppUnits(devPxSize,
                                                    appUnitsPerDevPx));

      // Note: Can't use DrawSingleUnscaledImage for the TYPE_VECTOR case.
      // That method needs our image to have a fixed native width & height,
      // and that's not always true for TYPE_VECTOR images.
      // FIXME We should use the return value, see bug 1258510.
      Unused << nsLayoutUtils::DrawSingleImage(
        aContext,
        PresContext(),
        mImageContainer,
        nsLayoutUtils::GetGraphicsFilterForFrame(this),
        destRect,
        aDirtyRect ? dirtyRect : destRect,
        &context,
        drawFlags);
    } else { // mImageContainer->GetType() == TYPE_RASTER
      // FIXME We should use the return value, see bug 1258510.
      Unused << nsLayoutUtils::DrawSingleUnscaledImage(
        aContext,
        PresContext(),
        mImageContainer,
        nsLayoutUtils::GetGraphicsFilterForFrame(this),
        nsPoint(0, 0),
        aDirtyRect ? &dirtyRect : nullptr,
        drawFlags);
    }

    if (opacity != 1.0f || StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
      aContext.PopGroupAndBlend();
    }
    // gfxContextAutoSaveRestore goes out of scope & cleans up our gfxContext
  }

  return rv;
}