Пример #1
0
nsresult
nsSVGMarkerFrame::PaintMark(gfxContext& aContext,
                            const gfxMatrix& aToMarkedFrameUserSpace,
                            nsSVGPathGeometryFrame *aMarkedFrame,
                            nsSVGMark *aMark, float aStrokeWidth)
{
  // If the flag is set when we get here, it means this marker frame
  // has already been used painting the current mark, and the document
  // has a marker reference loop.
  if (mInUse)
    return NS_OK;

  AutoMarkerReferencer markerRef(this, aMarkedFrame);

  SVGMarkerElement *marker = static_cast<SVGMarkerElement*>(mContent);
  if (!marker->HasValidDimensions()) {
    return NS_OK;
  }

  const nsSVGViewBoxRect viewBox = marker->GetViewBoxRect();

  if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) {
    // We must disable rendering if the viewBox width or height are zero.
    return NS_OK;
  }

  mStrokeWidth = aStrokeWidth;
  mX = aMark->x;
  mY = aMark->y;
  mAutoAngle = aMark->angle;
  mIsStart = aMark->type == nsSVGMark::eStart;

  Matrix viewBoxTM = marker->GetViewBoxTransform();

  Matrix markerTM = marker->GetMarkerTransform(mStrokeWidth, mX, mY,
                                               mAutoAngle, mIsStart);

  gfxMatrix markTM = ThebesMatrix(viewBoxTM) * ThebesMatrix(markerTM) *
                     aToMarkedFrameUserSpace;

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


  nsIFrame* kid = GetAnonymousChildFrame(this);
  nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
  // The CTM of each frame referencing us may be different.
  SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED);
  nsSVGUtils::PaintFrameWithEffects(kid, aContext, markTM);

  if (StyleDisplay()->IsScrollableOverflow())
    aContext.Restore();

  return NS_OK;
}
nsresult
nsSVGForeignObjectFrame::PaintSVG(gfxContext& aContext,
                                  const gfxMatrix& aTransform,
                                  const nsIntRect* aDirtyRect)
{
  NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
               (mState & NS_FRAME_IS_NONDISPLAY),
               "If display lists are enabled, only painting of non-display "
               "SVG should take this code path");

  if (IsDisabled())
    return NS_OK;

  nsIFrame* kid = GetFirstPrincipalChild();
  if (!kid)
    return NS_OK;

  if (aTransform.IsSingular()) {
    NS_WARNING("Can't render foreignObject element!");
    return NS_ERROR_FAILURE;
  }

  nsRect kidDirtyRect = kid->GetVisualOverflowRect();

  /* Check if we need to draw anything. */
  if (aDirtyRect) {
    NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
                 (mState & NS_FRAME_IS_NONDISPLAY),
                 "Display lists handle dirty rect intersection test");
    // Transform the dirty rect into app units in our userspace.
    gfxMatrix invmatrix = aTransform;
    DebugOnly<bool> ok = invmatrix.Invert();
    NS_ASSERTION(ok, "inverse of non-singular matrix should be non-singular");

    gfxRect transDirtyRect = gfxRect(aDirtyRect->x, aDirtyRect->y,
                                     aDirtyRect->width, aDirtyRect->height);
    transDirtyRect = invmatrix.TransformBounds(transDirtyRect);

    kidDirtyRect.IntersectRect(kidDirtyRect,
      nsLayoutUtils::RoundGfxRectToAppRect(transDirtyRect,
                       PresContext()->AppUnitsPerCSSPixel()));

    // XXX after bug 614732 is fixed, we will compare mRect with aDirtyRect,
    // not with kidDirtyRect. I.e.
    // int32_t appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel();
    // mRect.ToOutsidePixels(appUnitsPerDevPx).Intersects(*aDirtyRect)
    if (kidDirtyRect.IsEmpty())
      return NS_OK;
  }

  aContext.Save();

  if (StyleDisplay()->IsScrollableOverflow()) {
    float x, y, width, height;
    static_cast<nsSVGElement*>(mContent)->
      GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);

    gfxRect clipRect =
      nsSVGUtils::GetClipRectForFrame(this, 0.0f, 0.0f, width, height);
    nsSVGUtils::SetClipRect(&aContext, aTransform, clipRect);
  }

  // SVG paints in CSS px, but normally frames paint in dev pixels. Here we
  // multiply a CSS-px-to-dev-pixel factor onto aTransform so our children
  // paint correctly.
  float cssPxPerDevPx = PresContext()->
    AppUnitsToFloatCSSPixels(PresContext()->AppUnitsPerDevPixel());
  gfxMatrix canvasTMForChildren = aTransform;
  canvasTMForChildren.Scale(cssPxPerDevPx, cssPxPerDevPx);

  aContext.Multiply(canvasTMForChildren);

  uint32_t flags = nsLayoutUtils::PAINT_IN_TRANSFORM;
  if (SVGAutoRenderState::IsPaintingToWindow(aContext.GetDrawTarget())) {
    flags |= nsLayoutUtils::PAINT_TO_WINDOW;
  }
  nsRenderingContext rendCtx(&aContext);
  nsresult rv = nsLayoutUtils::PaintFrame(&rendCtx, kid, nsRegion(kidDirtyRect),
                                          NS_RGBA(0,0,0,0), flags);

  aContext.Restore();

  return rv;
}
void
nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext,
                                              nsIFrame* aFrame,
                                              const nsRect& aDirtyRect,
                                              const nsRect& aBorderArea,
                                              nsDisplayListBuilder* aBuilder,
                                              LayerManager *aLayerManager)
{
#ifdef DEBUG
  NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) ||
               (NS_SVGDisplayListPaintingEnabled() &&
                !(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)),
               "Should not use nsSVGIntegrationUtils on this SVG frame");
#endif

  /* SVG defines the following rendering model:
   *
   *  1. Render geometry
   *  2. Apply filter
   *  3. Apply clipping, masking, group opacity
   *
   * We follow this, but perform a couple of optimizations:
   *
   * + Use cairo's clipPath when representable natively (single object
   *   clip region).
   *
   * + Merge opacity and masking if both used together.
   */

  const nsIContent* content = aFrame->GetContent();
  bool hasSVGLayout = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
  if (hasSVGLayout) {
    nsISVGChildFrame *svgChildFrame = do_QueryFrame(aFrame);
    if (!svgChildFrame || !aFrame->GetContent()->IsSVGElement()) {
      NS_ASSERTION(false, "why?");
      return;
    }
    if (!static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
      return; // The SVG spec says not to draw _anything_
    }
  }

  float opacity = aFrame->StyleDisplay()->mOpacity;
  if (opacity == 0.0f) {
    return;
  }
  if (opacity != 1.0f &&
      hasSVGLayout && nsSVGUtils::CanOptimizeOpacity(aFrame)) {
    opacity = 1.0f;
  }

  /* Properties are added lazily and may have been removed by a restyle,
     so make sure all applicable ones are set again. */
  nsIFrame* firstFrame =
    nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
  nsSVGEffects::EffectProperties effectProperties =
    nsSVGEffects::GetEffectProperties(firstFrame);

  bool isOK = effectProperties.HasNoFilterOrHasValidFilter();
  nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);

  bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true;

  DrawTarget* drawTarget = aContext.GetDrawTarget();
  gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&aContext);

  nsPoint firstFrameOffset = GetOffsetToBoundingBox(firstFrame);
  nsPoint offsetToBoundingBox = aBuilder->ToReferenceFrame(firstFrame) - firstFrameOffset;
  if (!firstFrame->IsFrameOfType(nsIFrame::eSVG)) {
    /* Snap the offset if the reference frame is not a SVG frame,
     * since other frames will be snapped to pixel when rendering. */
    offsetToBoundingBox = nsPoint(
      aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.x),
      aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.y));
  }

  // After applying only "offsetToBoundingBox", aCtx would have its origin at
  // the top left corner of aFrame's bounding box (over all continuations).
  // However, SVG painting needs the origin to be located at the origin of the
  // SVG frame's "user space", i.e. the space in which, for example, the
  // frame's BBox lives.
  // SVG geometry frames and foreignObject frames apply their own offsets, so
  // their position is relative to their user space. So for these frame types,
  // if we want aCtx to be in user space, we first need to subtract the
  // frame's position so that SVG painting can later add it again and the
  // frame is painted in the right place.

  gfxPoint toUserSpaceGfx = nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame);
  nsPoint toUserSpace(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)),
                      nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y)));
  nsPoint offsetToUserSpace = offsetToBoundingBox - toUserSpace;

  NS_ASSERTION(hasSVGLayout || offsetToBoundingBox == offsetToUserSpace,
               "For non-SVG frames there shouldn't be any additional offset");

  gfxPoint devPixelOffsetToUserSpace =
    nsLayoutUtils::PointToGfxPoint(offsetToUserSpace,
                                   aFrame->PresContext()->AppUnitsPerDevPixel());
  aContext.SetMatrix(aContext.CurrentMatrix().Translate(devPixelOffsetToUserSpace));

  gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(aFrame);

  const nsStyleSVGReset *svgReset = firstFrame->StyleSVGReset();
  // Keep moving forward even if svgMaskFrame is nullptr or isOK is false.
  // This source is not a svg mask, but it still can be a correct mask image.
  nsSVGMaskFrame *svgMaskFrame = effectProperties.GetMaskFrame(&isOK);

  bool complexEffects = false;
  bool hasValidLayers = svgReset->mMask.HasLayerWithImage();

  // These are used if we require a temporary surface for a custom blend mode.
  RefPtr<gfxContext> target = &aContext;
  IntPoint targetOffset;

  /* Check if we need to do additional operations on this child's
   * rendering, which necessitates rendering into another surface. */
  if (opacity != 1.0f ||  (clipPathFrame && !isTrivialClip)
      || aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL
      || svgMaskFrame || hasValidLayers) {
    complexEffects = true;

    aContext.Save();
    nsRect clipRect =
      aFrame->GetVisualOverflowRectRelativeToSelf() + toUserSpace;
    aContext.Clip(NSRectToSnappedRect(clipRect,
                                  aFrame->PresContext()->AppUnitsPerDevPixel(),
                                  *drawTarget));

    Matrix maskTransform;
    RefPtr<SourceSurface> maskSurface;
    if (svgMaskFrame) {
      maskSurface = svgMaskFrame->GetMaskForMaskedFrame(&aContext,
                                                     aFrame,
                                                     cssPxToDevPxMatrix,
                                                     opacity,
                                                     &maskTransform);
    } else if (hasValidLayers) {
      gfxRect clipRect = aContext.GetClipExtents();
      {
        gfxContextMatrixAutoSaveRestore matRestore(&aContext);

        aContext.SetMatrix(gfxMatrix());
        clipRect = aContext.GetClipExtents();
      }
      IntRect drawRect = RoundedOut(ToRect(clipRect));
      RefPtr<DrawTarget> targetDT = aContext.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::A8);
      if (!targetDT) {
        aContext.Restore();
        return;
      }

      RefPtr<gfxContext> target = new gfxContext(targetDT);
      target->SetMatrix(matrixAutoSaveRestore.Matrix() * gfxMatrix::Translation(-drawRect.TopLeft()));

      // Generate mask surface.
      uint32_t flags = aBuilder->GetBackgroundPaintFlags() |
                       nsCSSRendering::PAINTBG_MASK_IMAGE;
      nsRenderingContext rc(target);
      nsCSSRendering::PaintBackgroundWithSC(aFrame->PresContext(),
                                            rc,
                                            aFrame,
                                            aDirtyRect,
                                            aBorderArea,
                                            firstFrame->StyleContext(),
                                            *aFrame->StyleBorder(),
                                            flags,
                                            nullptr,
                                            -1);
      maskSurface = targetDT->Snapshot();

      // Compute mask transform.
      Matrix mat = ToMatrix(aContext.CurrentMatrix());
      mat.Invert();
      maskTransform = Matrix::Translation(drawRect.x, drawRect.y) * mat;
    }

    if ((svgMaskFrame || hasValidLayers) && !maskSurface) {
      // Entire surface is clipped out.
      aContext.Restore();
      return;
    }

    if (aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
      // Create a temporary context to draw to so we can blend it back with
      // another operator.
      gfxRect clipRect;
      {
        gfxContextMatrixAutoSaveRestore matRestore(&aContext);

        aContext.SetMatrix(gfxMatrix());
        clipRect = aContext.GetClipExtents();
      }

      IntRect drawRect = RoundedOut(ToRect(clipRect));

      RefPtr<DrawTarget> targetDT = aContext.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::B8G8R8A8);
      if (!targetDT) {
        aContext.Restore();
        return;
      }
      target = new gfxContext(targetDT);
      target->SetMatrix(aContext.CurrentMatrix() * gfxMatrix::Translation(-drawRect.TopLeft()));
      targetOffset = drawRect.TopLeft();
    }

    if (clipPathFrame && !isTrivialClip) {
      Matrix clippedMaskTransform;
      RefPtr<SourceSurface> clipMaskSurface = clipPathFrame->GetClipMask(aContext, aFrame, cssPxToDevPxMatrix,
                                                                         &clippedMaskTransform, maskSurface, maskTransform);

      if (clipMaskSurface) {
        maskSurface = clipMaskSurface;
        maskTransform = clippedMaskTransform;
      }
    }

    if (opacity != 1.0f || svgMaskFrame  || hasValidLayers ||
        (clipPathFrame && !isTrivialClip)) {
      target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity, maskSurface, maskTransform);
    }
  }

  /* If this frame has only a trivial clipPath, set up cairo's clipping now so
   * we can just do normal painting and get it clipped appropriately.
   */
  if (clipPathFrame && isTrivialClip) {
    aContext.Save();
    clipPathFrame->ApplyClipPath(aContext, aFrame, cssPxToDevPxMatrix);
  }

  /* Paint the child */
  if (effectProperties.HasValidFilter()) {
    RegularFramePaintCallback callback(aBuilder, aLayerManager,
                                       offsetToUserSpace);

    nsRegion dirtyRegion = aDirtyRect - offsetToBoundingBox;
    gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame);
    nsFilterInstance::PaintFilteredFrame(aFrame, target->GetDrawTarget(),
                                         tm, &callback, &dirtyRegion);
  } else {
    target->SetMatrix(matrixAutoSaveRestore.Matrix());
    BasicLayerManager* basic = static_cast<BasicLayerManager*>(aLayerManager);
    RefPtr<gfxContext> oldCtx = basic->GetTarget();
    basic->SetTarget(target);
    aLayerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, aBuilder);
    basic->SetTarget(oldCtx);
  }

  if (clipPathFrame && isTrivialClip) {
    aContext.Restore();
  }

  /* No more effects, we're done. */
  if (!complexEffects) {
    return;
  }

  if (opacity != 1.0f || svgMaskFrame || hasValidLayers ||
      (clipPathFrame && !isTrivialClip)) {
    target->PopGroupAndBlend();
  }

  if (aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
    RefPtr<DrawTarget> targetDT = target->GetDrawTarget();
    target = nullptr;
    RefPtr<SourceSurface> targetSurf = targetDT->Snapshot();

    aContext.SetMatrix(gfxMatrix()); // This will be restored right after.
    RefPtr<gfxPattern> pattern = new gfxPattern(targetSurf, Matrix::Translation(targetOffset.x, targetOffset.y));
    aContext.SetPattern(pattern);
    aContext.Paint();
  }

  aContext.Restore();
}